Подтвердить что ты не робот

Как выполнять вложенные транзакции в NHibernate?

Могу ли я выполнять вложенные транзакции в NHibernate и как их реализовать? Я использую SQL Server 2008, поэтому поддержка определенно в СУБД.

Я нахожу, что если я попробую что-то вроде этого:

using (var outerTX = UnitOfWork.Current.BeginTransaction())
{
    using (var nestedTX = UnitOfWork.Current.BeginTransaction())
    {
        ... do stuff
        nestedTX.Commit();
    }

    outerTX.Commit();
}

то к моменту, когда дело доходит до outerTX.Commit(), транзакция стала неактивной и приводит к исключению ObjectDisposedException в сеансе AdoTransaction.

Итак, мы должны создавать вложенные сеансы NHibernate? Или есть какой-то другой класс, который мы должны использовать для переноса транзакций (я слышал о TransactionScope, но я не уверен, что это такое)?

Теперь я использую реализацию Ayende UnitOfWork (спасибо Sneal).

Простите любую наивность в этом вопросе, я все еще новичок в NHibernate.

Спасибо!

EDIT. Я обнаружил, что вы можете использовать TransactionScope, например:

using (var transactionScope = new TransactionScope())
{
    using (var tx = UnitOfWork.Current.BeginTransaction())
    {
        ... do stuff
        tx.Commit();
    }

    using (var tx = UnitOfWork.Current.BeginTransaction())
    {
        ... do stuff
        tx.Commit();
    }

    transactionScope.Commit();
}

Однако я не так волнуюсь по этому поводу, так как он блокирует нас использованием SQL Server, а также я обнаружил, что если база данных удаленная, вам нужно беспокоиться о том, что MSDTC включен... еще один компонент, чтобы пойти не так. Вложенные транзакции настолько полезны и легко выполняются в SQL, что я вроде предположил, что NHibernate будет иметь какой-то способ эмуляции того же...

4b9b3361

Ответ 1

Сессии NHibernate не поддерживают вложенные транзакции.

Следующий тест всегда верен в версии 2.1.2:

var session = sessionFactory.Open();
var tx1 = session.BeginTransaction();
var tx2  = session.BeginTransaction();
Assert.AreEqual(tx1, tx2);

Вам нужно обернуть его в TransactionScope для поддержки вложенных транзакций.

MSDTC должен быть включен или вы получите сообщение об ошибке:

{"Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable DTC for network access in the security configuration for MSDTC using the Component Services Administrative tool."}

Ответ 2

Я боролся с этим какое-то время. У меня будет другая трещина.

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

Потому что я использую Rhino Commons Теперь я собираюсь попробовать рефакторинг, используя With.Transaction. В основном это позволяет нам писать код, как если бы транзакции были вложены, хотя на самом деле есть только один.

Например:

private Project CreateProject(string name)
{
    var project = new Project(name);
    With.Transaction(delegate
    {
        UnitOfWork.CurrentSession.Save(project);
    });
    return project;
}

private Sample CreateSample(Project project, string code)
{
    var sample = new Sample(project, code);
    With.Transaction(delegate
    {
        UnitOfWork.CurrentSession.Save(sample);
    });
    return sample;
}

private void Test_NoNestedTransaction()
{
  var project = CreateProject("Project 1");
}

private void TestNestedTransaction()
{
  using (var tx = UnitOfWork.Current.BeginTransaction())
  {
      try
      {
          var project = CreateProject("Project 6");
          var sample = CreateSample(project, "SAMPLE006", true);
      }
      catch
      {
          tx.Rollback();
          throw;
      }
      tx.Commit();
  }
}

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

В Test_NestedTransaction() мы создаем образец и проект. Если что-то пойдет не так, мы хотим, чтобы оба были отброшены назад. На самом деле код в CreateSample и CreateProject будет работать так же, как если бы не было никаких транзакций; это полностью внешняя транзакция, которая решает, откатываться или совершать, и делает это на основании того, выбрано ли исключение. Действительно, почему я использую вручную созданную транзакцию для внешней транзакции; поэтому у меня есть контроль над тем, нужно ли совершать или откатывать, а не просто дефолтировать на-exception-rollback-else-commit.

Вы можете достичь того же самого результата без Rhino.Commons, поставив много чего такого в своем коде:

if (!UnitOfWork.Current.IsInActiveTransaction)
{
  tx = UnitOfWork.Current.BeginTransaction();
}

_auditRepository.SaveNew(auditEvent);
if (tx != null)
{
  tx.Commit();
}

... и так далее. Но With.Transaction, несмотря на неуклюжесть необходимости создания анонимных делегатов, делает это довольно удобно.

Преимущество этого подхода в использовании TransactionScope (помимо зависимости от MSDTC) заключается в том, что в окончательной транзакции внешней транзакции должен быть только один поток к базе данных, независимо от того, сколько методов было вызвано между. Другими словами, нам не нужно записывать незафиксированные данные в базу данных по мере того, как мы идем, мы всегда просто записываем ее в локальный кэш NHibernate.

Короче говоря, это решение не обеспечивает окончательного контроля над вашими транзакциями, потому что он никогда не использует больше одной транзакции. Думаю, я могу согласиться с этим, поскольку вложенные транзакции никоим образом не поддерживаются повсеместно в каждой СУБД. Но теперь, возможно, я могу хотя бы написать код, не беспокоясь о том, есть ли у нас транзакция или нет.

Ответ 3

Как предположил Сатиш, в NHibernate не поддерживаются вложенные транзакции. Я не сталкивался с ситуациями, когда нужны вложенные транзакции, но, конечно, я столкнулся с проблемами, когда мне приходилось игнорировать создание транзакций, если другие уже были активны в других подразделениях.

В приведенной ниже ссылке блога приведен пример реализации для NHibernate, но он также должен работать на SQL-сервере: http://rajputyh.blogspot.com/2011/02/nested-transaction-handling-with.html

Ответ 4

Эта реализация не поддерживает вложенность, если вы хотите, чтобы вложенность использовала реализация Ayende UnitOfWork. Другая проблема с используемой вами реализацией (по крайней мере для веб-приложений) заключается в том, что она хранится на экземпляре ISession в статической переменной.

Я только что переписал наш UnitOfWork вчера по этим причинам, он был первоначально основан на Габриэле.

Мы не используем UnitOfWork.Current.BeginTransaction(), мы используем UnitofWork.TransactionalFlush(), который создает отдельную транзакцию в самом конце, чтобы сразу очистить все изменения.

using (var uow = UnitOfWork.Start())
{
     var entity = repository.Get(1);
     entity.Name = "Sneal";
     uow.TransactionalFlush();
}