Используя транзакции или SaveChanges(false) и AcceptAllChanges()?

StackOverflow https://stackoverflow.com/questions/815586

Вопрос

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

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

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

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

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

Это было полезно?

Решение

Большую часть времени работает с 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 где-нибудь.

Видишь мой запись в блоге для большего.

Другие советы

Если вы используете 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
        } 
    } 
} 

Поскольку некоторая база данных может выдать исключение в 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(); 
    } 
  } 
} 
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top