Я использую EF Core 1.0 (ранее известное объявление EF7) и ASP.NET Core 1.0 (ранее известный как ASP.NET 5) для RESTful API ,Блок управления Entity Framework Core 1.0 с промежуточным программным обеспечением Asp.Net Core или фильтром Mvc
Я бы хотел, чтобы часть работы была привязана к HTTP-запросу таким образом, что при ответе на HTTP-запрос либо все изменения, внесенные в DbContext, будут сохранены в базе данных, либо никто не будет сохранен (например, если было какое-то исключение).
В прошлом я использовал WebAPI2 для этой цели с помощью NHibernate с помощью фильтра действий, в котором я начинаю транзакцию при выполнении действия, и при выполнении действия завершаю транзакцию и закрываю сеанс. Это было рекомендовано в http://isbn.directory/book/9781484201107
Однако теперь я использую Asp.Net Core (с Asp.Net Core Mvc, хотя это не должно быть релевантно) и Entity Framework, который, я понял, уже реализует единицу работы.
Я думаю, что наличие промежуточного программного обеспечения, подключенного к конвейеру ASP.NET (до MVC), было бы правильным путем. Таким образом, запрос будет идти:
PIPELINE ASP.NET: MyUnitOfWorkMiddleware ==> MVC контроллер ==> Repository ==> MVC контроллер ==> MyUnitOfWorkMiddleware
Я имел в виду иметь это промежуточное программное сохранить DbContext если исключение не произошло, так что в моих реализациях репозитория мне даже не нужно делать dbcontext.SaveChanges()
, и все будет как централизованная транзакция. В псевдокоде я думаю, что это было бы примерно так:
class MyUnitOfWorkMiddleware
{
//..
1-get an instance of DbContext for this request.
try {
2-await the next item in the pipeline.
3-dbContext.SaveChanges();
}
catch (Exception e) {
2.1-rollback changes (simply by ignoring context)
2.2-return an http error response
}
}
Имеет ли это смысл? Есть ли у кого-нибудь пример чего-то подобного? Я не могу найти хорошую практику или рекомендации по этому поводу.
Кроме того, если я пойду с этим подходом на моем уровне контроллера MVC, у меня не будет доступа к любому идентификатору ресурса, созданному базой данных при отправке нового ресурса, поскольку идентификатор не будет сгенерирован до тех пор, пока изменения dbContext не будут сохранены (позже в конвейере в моем промежуточном ПО ПОСЛЕ завершения работы контроллера). Что делать, если мне нужно получить доступ к вновь созданному идентификатору ресурса в моем контроллере?
Любой совет будет рад!
UPDATE 1: Я нашел проблему с моим подходом использовать промежуточное программное обеспечение для достижения этой цели, так как экземпляр DbContext в ПО промежуточного слоя не такой же, как во время MVC (и хранилищ) жизни. Смотрите вопрос Entity Framework Core 1.0 DbContext not scoped to http request
UPDATE 2: Я еще не нашел хорошее решение. В основном это мои варианты до сих пор:
- Сохраните изменения в БД как можно скорее. Это означает, что он сохраняет его в самой реализации репозитория. Проблема с этим подходом заключается в том, что для запроса Http, возможно, я хочу использовать несколько репозиториев (т.е.e: сохраните что-нибудь в базе данных, а затем загрузите blob в облачное хранилище), и для того, чтобы иметь единицу работы, мне пришлось бы реализовать репозиторий, который имеет дело с несколькими объектами или даже с более чем одним методом persistance (DB и Blob Хранение), которая побеждает целую цель
Внесите Action Filter, где я завершаю выполнение всего действия в транзакции БД. В конце выполнения действия контроллера, если нет исключений, я беру chanches в DB, но если есть исключения, я откатываюсь и отбрасываю контекст. Проблема в том, что для действия моего контроллера может потребоваться сформированный Идентификатор Entity, чтобы вернуть его клиенту http (то есть: если я получу POST/api/cars, я хотел бы вернуть 201 Accepted с заголовком местоположения, который идентифицирует новый ресурс, созданный в/api/cars/123 и Id 123, еще не будет доступен, поскольку объект не был сохранен в БД, а идентификатор все еще является временным 0). Пример в действии контроллера для запроса POST глагола:
return CreatedAtRoute("GetCarById", new { carId= carSummaryCreated.Id }, carSummaryCreated); //carSummaryCreated.Id would be 0 until the changes are saved in DB
Как я мог бы иметь действие на целом диспетчерском завернутом в транзакции БДА и в то же время иметь в своем распоряжении любого Id генерируемой базы данных в порядке вернуть его в Http Response от контроллера? Или .. Есть ли какой-либо элегантный способ переписать ответ HTTP и установить Id на уровне фильтра действий после того, как были изменены изменения БД?
UPDATE 3: Согласно nathanaldensr «s комментарий я мог бы получить лучшее из обоих миров (оберточной выполнение действия моего контроллера в транзакции БД _ UOW, а также зная идентификатор нового ресурса, созданного еще до БД фиксирует изменения) с помощью генерируемых кода. Гиды вместо этого полагаются на базу данных для создания Guid.
Это хорошее решение благодаря – iberodev
Мне нравится, что вы можете украсить любое действие или контроллер с помощью блока рабочего фильтра, чтобы обернуть все в транзакции в течение жизненного цикла mvc. – iberodev
Проблема остается в том, что делать, когда вам нужен идентификатор объекта, чтобы продолжать делать вещи в рамках одного и того же HTTP-запроса. Идентификатор не будет генерироваться до тех пор, пока изменения в БД не будут выполнены. И при таком подходе изменения не сохраняются до тех пор, пока контроллер не завершит выполнение. – iberodev