2009-07-28 2 views
17

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

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

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's UnitOfWork implementation (спасибо 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 будет иметь некоторый способ подражать одному и тому же ...

+1

Вы смогли найти ответ? Как в конечном итоге сделать вложенные транзакции? – learning

+0

@ пользователь281180, вид. Я не нашел способа сделать это, но вы можете приблизиться к опыту. Сообщаем об этом в блоге: http://blog.constructionhive.com/2010/07/22/nested-transactions-and-nhibernate/ – Gavin

ответ

1

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

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

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

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

Да, я чувствую, что с самого начала я должен был использовать Rhino Commons ... Я думаю Я поехал с Габриэлем, чтобы я мог понять, как работала модель Unit Of Work, создавая ее с нуля (и это действительно помогло моему пониманию на самом деле), но, возможно, теперь пришло время поиграть с большими мальчиками ... спасибо , – Gavin

+0

@Sneal: Мы реализовали UnitOfWork от Ayende, используя Rhino.Commons, и это хорошо, но я все еще не понимаю, как работать вложенные транзакции.Код, описанный в моем исходном вопросе, действует точно так же, как и раньше (т. Е. Объекты транзакций, как представляется, разделяются). Можете ли вы дать мне какие-нибудь указания? Спасибо. – Gavin

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 будет работать так же, как если бы транзакций не было вообще; это полностью внешняя транзакция, которая решает, откатываться или совершать, и делает это на основании того, выбрано ли исключение. На самом деле именно поэтому я использую транзакцию, созданную вручную для внешней транзакции; поэтому у меня есть контроль над тем, нужно ли совершать или откатывать, а не просто по умолчанию on-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.

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

13

Сессии 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."}

+0

Значит ли это, даже если я создаю вложенные транзакции в NHibernate, они будут рассматриваться как одно внешнее соединение? –

2

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

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

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