EF: Как я могу дважды вызвать SaveChanges внутри транзакции?
Используя Entity Framework (сначала код в моем случае), у меня есть операция, которая требует от меня вызова SaveChanges для обновления одного объекта в БД, а затем SaveChanges снова для обновления другого объекта. (Мне нужно сначала SaveChanges, чтобы решить проблему, когда EF не может определить, какой объект для обновления сначала).
Я пробовал делать:
using (var transaction = new TransactionScope())
{
// Do something
db.SaveChanges();
// Do something else
db.SaveChanges();
tramsaction.Complete();
}
Когда я запустил это, я получаю исключение во втором вызове SaveChanges
, говоря, что "базовый поставщик отказался при открытии". Внутреннее исключение говорит о том, что MSDTC не включен на моей машине.
Теперь я видел сообщения в другом месте, которые описывают, как включить MSDTC, но мне кажется, что мне также необходимо включить доступ к сети и т.д. Это звучит как полный перебор, потому что других баз данных не задействовано, не говоря уже о другие серверы. Я не хочу делать что-то, что сделает мое приложение менее безопасным (или медленнее).
Конечно, должен быть более легкий способ сделать это (в идеале без MSDTC)?
Ответы
Ответ 1
Вероятно, это связано с двумя различными подключениями, используемыми в вашей транзакции. Попробуйте вручную управлять подключением для работы:
var objectContext = ((IObjectContextAdapter)db).ObjectContext;
try {
object.Context.Connection.Open();
using (var transaction = new TransactionScope()) {
// Do something
db.SaveChanges();
// Do something else
db.SaveChanges();
transaction.Complete();
}
} finally {
objectContext.Connection.Close();
}
Ответ 2
Я знаю это как-то вроде позднего ответа, но я нашел полезным поделиться.
Теперь в EF6 это легче понять, используя dbContext.Database.BeginTransaction()
вот так:
using (var context = new BloggingContext())
{
using (var dbContextTransaction = context.Database.BeginTransaction())
{
try
{
// do your changes
context.SaveChanges();
// do another changes
context.SaveChanges();
dbContextTransaction.Commit();
}
catch (Exception)
{
dbContextTransaction.Rollback();
}
}
}
для более подробной информации смотрите this
снова в EF6 Вперед
Ответ 3
Вызывая SaveChanges(), поскольку вы заставляете данные сохраняться в базе данных и EF, чтобы забыть об изменениях, которые она только что сделала.
Фокус в том, чтобы использовать SaveChanges (false), чтобы изменения сохранялись в БД, но EF не забывает об изменениях, которые он делает, делая возможным ведение журнала/повторение.
var scope = new TransactionScope(
TransactionScopeOption.RequiresNew,
new TransactionOptions() { IsolationLevel = IsolationLevel.Serializable }
);
using (scope)
{
Entities context1 = new Entities();
// Do Stuff
context1.SaveChanges(false);
Entities context2 = new Entities();
// Do Stuff
context2.SaveChanges(false);
scope.Complete();
context1.AcceptAllChanges();
context2.AcceptAllChanges();
}
P.S. Как только у вас откроется более одного соединения внутри транзакции, он будет переходить на DTC.
Ответ 4
Для окончательного ответа, выбранного выше, есть опечатка. Исправленная строка ниже:
objectContext.Connection.Open();
Также необходимо добавить ссылки для System.Data.Entity.Infrastructure и System.Transactions