Использование транзакций или SaveChanges (false) и AcceptAllChanges ()? - PullRequest
334 голосов
/ 03 мая 2009

Я расследовал транзакции, и оказалось, что они заботятся о себе в EF, пока я передаю false на SaveChanges() и затем вызываю AcceptAllChanges(), если нет ошибок:

SaveChanges(false);
// ...
AcceptAllChanges();

Что, если что-то пойдет не так? мне не нужно откатывать или, как только мой метод выходит из области видимости, транзакция завершена?

Что происходит с любыми столбцами отступов, которые были назначены в середине транзакции? Я предполагаю, что если кто-то еще добавил запись после моей, прежде чем моя испортилась, это означает, что будет отсутствовать значение Identity.

Есть ли причина использовать стандартный класс TransactionScope в моем коде?

Ответы [ 3 ]

441 голосов
/ 03 мая 2009

С Entity Framework большую часть времени достаточно SaveChanges(). Это создает транзакцию или участвует в любой внешней транзакции и выполняет всю необходимую работу в этой транзакции.

Иногда, хотя спаривание SaveChanges(false) + AcceptAllChanges() полезно.

Самое полезное место для этого - ситуации, когда вы хотите выполнить распределенную транзакцию в двух разных контекстах.

т.е. как то так (плохо):

using (TransactionScope scope = new TransactionScope())
{
    //Do something with context1
    //Do something with context2

    //Save and discard changes
    context1.SaveChanges();

    //Save and discard changes
    context2.SaveChanges();

    //if we get here things are looking good.
    scope.Complete();
}

Если context1.SaveChanges() успешно, но context2.SaveChanges() не удалось, вся распределенная транзакция отменяется. Но, к сожалению, Entity Framework уже отклонил изменения на context1, поэтому вы не можете воспроизвести или эффективно зарегистрировать ошибку.

Но если вы измените свой код так:

using (TransactionScope scope = new TransactionScope())
{
    //Do something with context1
    //Do something with context2

    //Save Changes but don't discard yet
    context1.SaveChanges(false);

    //Save Changes but don't discard yet
    context2.SaveChanges(false);

    //if we get here things are looking good.
    scope.Complete();
    context1.AcceptAllChanges();
    context2.AcceptAllChanges();

}

Хотя вызов SaveChanges(false) отправляет необходимые команды в базу данных, сам контекст не изменяется, поэтому вы можете сделать это снова, если необходимо, или вы можете запросить ObjectStateManager, если хотите.

Это означает, что если транзакция фактически выдает исключение, которое вы можете компенсировать, повторяя попытку или регистрируя состояние каждого контекста ObjectStateManager где-то.

Подробнее см. мой блог .

108 голосов
/ 29 июля 2014

Если вы используете EF6 (Entity Framework 6+), это изменилось для вызовов базы данных в SQL.
Смотри: http://msdn.microsoft.com/en-us/data/dn456843.aspx

использовать context.Database.BeginTransaction.

Из MSDN:

using (var context = new BloggingContext()) 
{ 
    using (var dbContextTransaction = context.Database.BeginTransaction()) 
    { 
        try 
        { 
            context.Database.ExecuteSqlCommand( 
                @"UPDATE Blogs SET Rating = 5" + 
                    " WHERE Name LIKE '%Entity Framework%'" 
                ); 

            var query = context.Posts.Where(p => p.Blog.Rating >= 5); 
            foreach (var post in query) 
            { 
                post.Title += "[Cool Blog]"; 
            } 

            context.SaveChanges(); 

            dbContextTransaction.Commit(); 
        } 
        catch (Exception) 
        { 
            dbContextTransaction.Rollback(); //Required according to MSDN article 
            throw; //Not in MSDN article, but recommended so the exception still bubbles up
        } 
    } 
} 
0 голосов
/ 06 марта 2015

Поскольку некоторая база данных может выдать исключение в dbContextTransaction.Commit (), лучше это:

using (var context = new BloggingContext()) 
{ 
  using (var dbContextTransaction = context.Database.BeginTransaction()) 
  { 
    try 
    { 
      context.Database.ExecuteSqlCommand( 
          @"UPDATE Blogs SET Rating = 5" + 
              " WHERE Name LIKE '%Entity Framework%'" 
          ); 

      var query = context.Posts.Where(p => p.Blog.Rating >= 5); 
      foreach (var post in query) 
      { 
          post.Title += "[Cool Blog]"; 
      } 

      context.SaveChanges(false); 

      dbContextTransaction.Commit(); 

      context.AcceptAllChanges();
    } 
    catch (Exception) 
    { 
      dbContextTransaction.Rollback(); 
    } 
  } 
} 
...