Я использую mehdime/DbContextScope как documented here, чтобы реализовать решение для обновления/импорта массового пользователя для нашего продукта.DbContextScope Transaction
Под заголовком «изменения, сохраняются только тогда, когда SaveChanges() называется», Мехди говорится в примере кода:
Не называйте SaveChanges() до завершения бизнес-операции завершена - т.е. нет частичное или промежуточное сохранение. SaveChanges() необходимо называть ровно один раз за транзакцию.
Если вы окажетесь необходимости вызвать SaveChanges() несколько раз в бизнес-операции, то это означает, что вы на самом деле реализации нескольких бизнес-операций в рамках одного метода обслуживания .
Однако, я хочу добавить новую строку, получить ее сгенерированный идентификатор (столбец IDENTITY), а затем добавить запись журнала аудита в базу данных, ссылающуюся на сгенерированный идентификатор. Для этого мне нужно выполнить промежуточный SaveChanges()
, чтобы EF выполнил INSERT и получил идентификатор, однако я хочу сделать это в одной атомной транзакции.
Я что-то упустил или это действительно возможно, не нарушая правила Меджи?
Вот мой пример кода (который в настоящее время нарушает правило)
/// <summary>
/// Creates a new Location
/// </summary>
/// <param name="orgID">ID of Organisation</param>
/// <param name="name">Location Name</param>
/// <returns></returns>
public IActionResult Create(int orgID, string name)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentNullException(nameof(name));
// New Location
var location = new Location {
OrganisationID = orgID,
Name = name,
Enabled = true
};
using (var dbContextScope = _dbContextScopeFactory.Create())
{
var ctx = dbContextScope.DbContexts.Get<PlatformEntities2014>();
// Duplicate name?
var existing = ctx.Locations.Where(l => l.OrganisationID == orgID && l.Name == name).FirstOrDefault();
if (existing != null)
return new ActionResult(ActionResultCode.ErrorAlreadyExists) { ReferencedObject = existing };
ctx.Locations.Add(location);
// ---POSITION A---
dbContextScope.SaveChanges(); // (Assigns location.ID)
// Log
GeneralLog log = new GeneralLog() {
DateTime = DateTime.Now,
Code = "LOC",
SubCode = "NEW",
OrganisationID = orgID,
Information = $"Location {location.ID} \"{location.Name}\" created during Bulk Refresh."
};
ctx.GeneralLogs.Add(log);
// ---POSITION B---
dbContextScope.SaveChanges();
return new ActionResult(ActionResultCode.Success) { ReferencedObject = location };
}
}
было бы приемлемо, чтобы заменить первый dbContextScope.SaveChanges();
в положении А с ctx.SaveChanges();
для получения идентификатора и вызвать dbContextScope.SaveChanges();
на ПОЗИЦИИ B?
Спасибо.
Как ваши отношения EF настраиваются в контексте. Если они правильно настроены, так как местоположение связано с журналом через отношение внешнего ключа. Вы можете просто вызвать savechanges() в позиции 2, а EF должен автоматически подключить идентификаторы для вас. –
@ Увидеть, к сожалению, как видно из примера кода, таблица «Общий лог» является общим журналом и не имеет прямого отношения к таблице «Местоположение». Мы хотим зарегистрировать идентификатор и имя местоположения в 'Информационное поле varchar (...)'. 'GeneralLog' записывает много информации о множестве разных вещей и не является специфичным для местоположения. Я понимаю ваше предложение, но отношения в этом случае не существуют. –
Должен был прочитать это немного лучше. Хорошо, в этом случае это две отдельные бизнес-операции. Посмотрите на это таким образом, что ваша транзакция не считается неудачной, если она не создает «GeneralLog». Поэтому я бы сказал, что делаю это как две отдельные транзакции. –