Как сохранить список в EF 6 с помощью транзакций?

#c# #entity-framework

#c# #entity-framework

Вопрос:

Я пытаюсь найти наиболее правильный способ сохранения списка заказов и одного из платежей с использованием EF6. Я хотел бы знать, верен ли приведенный ниже пример кода, например, если цикл foreach находится в правильном месте?

 using (ContextDB db = new ContextDB())
{
   foreach (Order item in orders)
      {
          using (var dbContextTransaction = db.Database.BeginTransaction())    
          {
              try
              {               

                  // some logic here to retrieve related payment, etc...
                   ...

                   db.Orders.Add(item);
                   db.Payments.Add(pmntRcvd) 
                   db.SaveChanges();   

                   dbContextTransaction.Commit();
               }
               catch
               {
                   // Code to log the exception
                   ...                        
                }  
           }   
      }
}
  

Ответ №1:

  1. Вы сохраняете элементы в отдельных транзакциях. Это должно быть очевидно: вы создаете одну транзакцию для каждого элемента.
  2. В этом нет необходимости Rollback . Если вы этого не Commit сделаете, всегда произойдет откат. Просто удалите весь код обработки ошибок.
  3. Вы проглатываете исключения, чтобы никогда не узнать о проблемах.
  4. Я не понимаю, почему вы SaveChanges так часто звоните. Почему не один раз в конце?

Комментарии:

1. 3. Я обновляю вопрос. Существует код для регистрации исключения и элемента, который его вызвал4. Потому что мне нужно убедиться, что элемент сохраняется после его обработки. Что произойдет, если я вызову SaveChanges() только один раз в конце и возникнет проблема? Или, может быть, я еще не очень хорошо понимаю EF6.

2. (3) вы все равно должны удалить откат и добавить throw; . (4) в случае ошибки ничего не будет сохранено. Я понимаю, что это ваше намерение. Вызовите его в конце транзакции или раньше, если необходимо.

3. Спасибо за информацию. Я удалил первую db.SaveChanges() и откат. Пока, похоже, все работает так, как ожидалось. Должен ли я также удалить используемую транзакцию или сохранить ее?

4. Ответ сложнее, чем вы думаете. SaveChanges всегда является атомарным, поэтому, если у вас есть только один, вам не нужен tran для атомарности. Но . Пул сетевых подключений имеет недостаток в том, что каждое соединение использует непредсказуемый уровень изоляции при запуске. Рекомендуется всегда использовать явные транзакции с указанным уровнем изоляции.

Ответ №2:

Почему бы не использовать одну транзакцию для всех заказов (например, вывести транзакцию за пределы цикла)?

Если вы готовы принять сценарий, в котором некоторые заказы сохраняются, а некоторые нет, это нормально, но, как сказал #usr, вам нужно сохранить изменения только один раз перед фиксацией транзакции, и вы должны зарегистрировать отклоненные заказы.

Несколько сохранений необходимы, если вам нужно, например, получить идентификатор вновь созданного объекта.

Комментарии:

1. Я попытался поместить код в цикл using transaction, но он выдает исключение после первой фиксации. (Я думаю, это потому, что фиксация обнуляет транзакцию после ее фиксации).

2. Если у вас работает одна транзакция, вам даже не нужно явно создавать транзакцию, потому что EF создает неявную транзакцию для контекста. Тогда ваш код должен выглядеть следующим образом: использование db { for { } db.SaveChanges(); }