2015-12-08 2 views
8

В настоящее время я использую TransactionScope для управления транзакциями на моем уровне данных, но у меня возникли проблемы с вложенными транзакциями и асинхронным способом, при которых соединение, кажется, закрывается во время вложенной транзакции или транзакция продвигается в MSDTC. Я не нашел точной проблемы, но после прочтения это выглядит так, что этот сценарий не поддерживается particuarly well и вместо этого я должен использовать Database.BeginTransaction().Взаимодействие с вложенными транзакциями в EF6

Моя проблема заключается в том, что я не могу найти информацию о том, как Database.BeginTransaction() работает с вложенными транзакциями, особенно в моем сценарии, когда я хочу использовать внешнюю транзакцию, а не создавать новую. Мое подозрение в том, что он не предназначен для работы таким образом, и если я хочу управлять вложенными транзакциями, я должен абстрагироваться от управления транзакциями, чтобы дать мне больше контроля.

Не желая добавлять лишние слои абстракций Я хотел знать, есть ли у кого-либо опыт в этой области и может подтвердить поведение Database.BeginTransaction(), когда он вложен в другую транзакцию?

Дополнительная информация о моей DAL: на основе CQS рисунка, я, как правило, чтобы инкапсулировать Db код, связанный в командных или запроса обработчиков, поэтому упрощенный/надуманный пример того, как происходит это вложенности будет:

public class AddBlogPostHandler 
{ 
    private readonly MyDbContext _myDbContext; 

    public AddBlogPostHandler(MyDbContext myDbContext) 
    { 
     _myDbContext = myDbContext; 
    } 

    public async Task ExecuteAsync(AddBlogPostCommand command) 
    { 
     using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) 
     { 
      // .. code to create and add a draft blog post to the context 
      await _myDbContext.SaveChangesAsync(); 

      var publishBlogPostCommand = new PublishBlogPostCommand(); 
      // ..set some variables on the PublishBlogPostCommand 
      await PublishBlogPostAsync(command); 

      scope.Complete(); 
     } 
    } 
} 

public class PublishBlogPostHandler 
{ 
    private readonly MyDbContext _myDbContext; 

    public PublishBlogPostHandler(MyDbContext myDbContext) 
    { 
     _myDbContext = myDbContext; 
    } 

    public async Task ExecuteAsync(PublishBlogPostCommand command) 
    { 
     using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) 
     { 
      // .. some code to do one set of update 
      await _myDbContext.SaveChangesAsync(); 

      // .. some other db updates that need to be run separately 
      await _myDbContext.SaveChangesAsync(); 

      scope.Complete(); 
     } 
    } 
} 

ответ

5

Там это не такая штука, как вложенные транзакции в том смысле, что внутренний может совершать или откатываться независимо. Вложенные транзакции действительно поддерживают только количество ссылок. При последнем фиксации мы получаем физическую фиксацию. При первом откате мы получаем физический откат. Просто убедитесь, что вы об этом знаете.

Важно избегать использования MSDTC. Это возможно как с TransactionScope, так и с BeginTransaction. С первым вам нужно явно указать соединение внутри области, чтобы EF не открывал новые соединения все время.

Как вы прочли в этом выпуске, это недостаток в EF (которого у L2S не было). Пожалуйста, найдите время, чтобы прокомментировать эту проблему, чтобы убедиться, что команда знает, что клиенты сталкиваются с этой проблемой.

особенно в моем сценарии, когда я хочу использовать внешнюю транзакцию, а не создавать новую.

Это идеальное решение для TransactionScope. Я думаю, что ваш переключатель на BeginTransaction основан на недоразумении. Возможно, вы можете уточнить в комментариях.

подтверждают поведение Database.BeginTransaction(), когда вложен в другой транзакции

Разъяснения в первом абзаце.

Дополнительная информация о моей DAL: на основе CQS рисунка, я, как правило, чтобы инкапсулировать Db код, связанный в командных или запроса обработчиков, поэтому упрощенный/надуманный пример того, как это гнездования происходит бы:

Код выглядит отлично, за исключением отсутствующего вызова db.Connection.Open() (как объяснялось выше).

Этот шаблон будет поддерживать выполнение нескольких запросов и команд в одной транзакции.Просто оберните вокруг себя еще одну область. Убедитесь, что вы не открываете соединения дважды, например. проверьте conn.State перед тем, как принять меры.

+0

Я думаю, что я действительно спрашиваю, создает ли вложенный вызов «BeginTransaction» новую транзакцию (эквивалентную «TransactionScopeOption.RequiresNew») или использует транзакцию с внешним использованием (эквивалентно «TransactionScopeOption.Required')? Помимо моей неспособности заставить ее работать, переход на «BeginTransaction» основывался на том, что это их рекомендация для EF6 +. Я открыт для того, чтобы заставить его работать с «TransactionScope», но если я открою соединение вручную, я получу исключение до завершения внешней транзакции (транзакция «завершена, но не была удалена») –

+1

BeginTransaction как Требуется. Нет способа иметь несколько транс в одном соединении. Поэтому он не может вести себя как RequiresNew. 'будучи их рекомендацией для EF6', я тоже это читал, но я думаю, что никаких аргументов не было. Странный совет. 'Я получаю исключение до завершения внешней транзакции. Я думаю, вам следует исследовать и исправить ошибку. Зачем бросать весь подход, если все, что вам нужно сделать, это исправить небольшую ошибку? – usr

+0

Отлично, я не знал, что у вас может быть только одна транзакция за соединение. Я просто не мог «TransactionScope» работать в моем сценарии, поэтому в итоге я написал небольшую абстракцию, которая означала, что у меня могут быть вложенные области транзакций и по-прежнему использовать «Database.BeginTransaction», чтобы открыть базовую транзакцию. Благодарим за помощь @usr –

Смежные вопросы