NHibernate, TransactionScope и блокировки - PullRequest
2 голосов
/ 20 августа 2010

Я пытаюсь использовать TransactionScope с NHibernate для вызова нескольких методов в одной транзакции.Методы хранилища данных выглядят так:

public virtual void Save(T dataObject) { try { using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead })) { this.session.SaveOrUpdate(dataObject); scope.Complete(); } } catch (Exception ex) { bool rethrow = ExceptionPolicy.HandleException(ex, "Data Layer Policy"); if (rethrow) { throw; } } }</p> <p>public T GetByNumber(string documentNumber) { T document = null; try { using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead })) { document = this.Session.CreateCriteria(typeof(T)) .Add(Restrictions.Eq("Number", documentNumber)) .UniqueResult(); scope.Complete(); } } catch (Exception ex) { bool rethrow = ExceptionPolicy.HandleException(ex, "Data Layer Policy"); if (rethrow) { throw; } } return document; }

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

Приложение, которое обновляет: const string DocumentNumber = "386774321"; Random randomGenerator = new Random(); using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead })) { using (BillingDocumentRepository billingDocumentRepository = new BillingDocumentRepository()) { BillingOrderData orderData = billingDocumentRepository.GetByNumber(DocumentNumber); orderData.Notes = randomGenerator.Next().ToString(); Console.WriteLine(string.Format("SECOND: {0}: Updated notes to {1}.", DateTime.Now.ToString("HH:mm:ss.fffff"), orderData.Notes)); Console.WriteLine(string.Format("SECOND: {0}: Updating order.", DateTime.Now.ToString("HH:mm:ss.fffff"))); Console.WriteLine(string.Format("SECOND: {0}: Going to sleep for 10000ms.", DateTime.Now.ToString("HH:mm:ss.fffff"))); Sleep(10000); // My custom sleep method because I didn't want to use Thread.Sleep for simulating long transaction billingDocumentRepository.Save(orderData); } Console.WriteLine(string.Format("SECOND: {0}: Going to sleep for 10000ms.", DateTime.Now.ToString("HH:mm:ss.fffff"))); Sleep(10000); Console.WriteLine(string.Format("SECOND: {0}: Completing transaction.", DateTime.Now.ToString("HH:mm:ss.fffff"))); scope.Complete(); }

Приложение, которое читает ту же строку в базе данных: while (true) { using (BillingDocumentRepository repository = new BillingDocumentRepository()) { Console.WriteLine(string.Format("MAIN: {0}: Getting document.", DateTime.Now.ToString("HH:mm:ss.fffff"))); BillingOrderData billingOrderData = repository.GetByNumber("386774321"); Console.WriteLine(string.Format("MAIN: {0}: Got order with notes {1}.", DateTime.Now.ToString("HH:mm:ss.fffff"), billingOrderData.Notes)); Sleep(1000); } }

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

Ответы [ 2 ]

1 голос
/ 09 июля 2018

Вы должны заблокировать при чтении. Блокировка позже "слишком поздно":

 document = this.Session.CreateCriteria(typeof(T))
            .Add(Restrictions.Eq("Number", documentNumber))
            .SetLockMode(LockMode.Upgrade)
            .SetTimeout(5)
            .UniqueResult();

Или:

 var doc = session.QueryOver<BillingDocument>()
               .Where(c => c.Number== "2233445")
                .Lock()
                .Upgrade
                .UnderlyingCriteria.
                 SetTimeout(5).
                 List().
                 FirstOrNull() as BillingDocument;
0 голосов
/ 20 августа 2010

Существует метод session.Lock(object).

Когда вы вызываете session.Save(object), NHibernate ничего не делает в базе данных, пока не будет сброшен.

Промывка выполняется (в зависимости от режима промывки, обычно это AutoFlush)

  • перед запросами (кроме Get и Load)
  • при явном вызове flush
  • при совершении транзакции (если соединение создается NH, я думаю)

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

В SQL Server, когда установлена ​​блокировка, транзакция чтения ожидает подтверждения транзакции обновления. Когда он фиксирует, он читает зафиксированные значения (когда вы находитесь в изоляции «Read Committed»).

...