Ответ 1
Если вы не предоставляете DbContext
уже открытое SQL-соединение, DbContext
откроет и закроет соединение для вас, когда вы вызываете SaveChanges
. В этом случае нет опасности поддерживать DbContext
вокруг, за исключением того, что объекты, которые DbContext
может удерживаться, могут находиться в недопустимом состоянии (поскольку это может быть причиной отказа SQL-запроса).
Здесь приведен пример DbContext
, который поддерживается открытым подключением и транзакцией SQL:
using (var connection = new SqlConnection("my connection"))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
using (var context = new DbContext(connection))
{
// Do useful stuff.
context.SaveChanges();
}
transaction.Commit();
}
}
Если вы укажете DbContext
с SqlConnection
, который выполняется в контексте транзакции, этот ответ.
Обратите внимание, что Entity Framework не будет создавать вложенную транзакцию. Он просто проверяет, связано ли соединение с пользовательской транзакцией. Если SaveChanges
уже выполняется в транзакции, транзакция не запускается. Однако Entity Framework не может определить, прервала ли база данных транзакцию из-за серьезного сбоя (например, блокировки базы данных). Поэтому, если первый вызов SaveChanges
завершился неудачей с чем-то вроде тупика, и вы поймали и вспомнили SaveChanges
, Entity Framework все еще думает, что он работает внутри транзакции.
Это означает, что этот второй вызов выполняется без транзакции, а это означает, что когда операция завершится неудачно, уже выполненные операторы НЕ будут откатны, так как транзакция отката не выполняется.
Проблема оборванной операции SaveChanges
могла быть предотвращена, если Entity Framework использовала вложенные транзакции, но она все равно не решила бы общую проблему согласованности.
Entity Framework создает для нас соединения и транзакции, когда мы не предоставляем их явно. Нам просто нужно/нужно напрямую указывать соединение и транзакцию, когда вызов SaveChanges
является частью более крупной общей транзакции. Поэтому даже если EF создала для нас вложенную транзакцию и совершила это, прежде чем вернуться из SaveChanges
, у нас возникнут проблемы, если мы будем называть SaveChanges
второй раз, так как эта "вложенная" транзакция фактически не вложена вообще. Когда EF совершает эту "вложенную" транзакцию, она фактически совершает единственную транзакцию, которая означает, что вся операция, в которой мы нуждались, является атомной, разорвана; все изменения, сделанные с помощью SaveChanges
, совершаются, а операции, которые могли возникнуть после этого вызова, не выполнялись. Очевидно, это нехорошее место.
Итак, моральная история состоит в том, что либо вы позволяете Entity Framework обрабатывать соединения и транзакции для вас, и вы можете повторно звонить на SaveChanges
без риска, или сами обрабатываете транзакции, и вам придется быстро работать, когда база данных выдает исключение; вы не должны называть SaveChanges
снова.