2016-12-23 46 views
0

Я разрабатываю Data Access Layer, используя NHibernate. Он будет использоваться в моем бизнес-логическом слое. Мое приложение представляет собой набор нескольких других приложений (ASP.NET MVC, Windows Services, Windows Forms, ASP.NET Web API), все они будут использовать один и тот же бизнес-логический уровень. Уровень бизнес-логики достигнет уровня доступа к данным. Приложения НЕ будут напрямую обращаться к Уровню доступа к данным.NHibernate: В какой сфере я должен использовать транзакцию?

Я знаю, что я НЕ должен зависеть от неявной транзакции и должен включать все вызовы базы данных (включая вызовы READ) в явной транзакции.

В моем понимании транзакция должна быть короткой. Если он живет долго, это может создать проблемы. См. 1 и 2.

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

С другой стороны, многие ресурсы (на StackOverflow и других сайтах) предлагают использовать транзакцию на каком-то более высоком уровне, например web-request (сеанс на запрос) в веб-приложении или «для оконных форм» в WinForms и т. Д. Это улучшает пакетную обработку запросов и лучше использовать UnitOfWork. Проблема с этим подходом заключается в том, что транзакции долговечны.

Путь 1] Вывести транзакцию в приложение и позволить ему обрабатывать ее.

Вариант 1 - Абонент может справиться с этим на уровне запроса. Недостатком этого подхода является то, что транзакции могут быть долговечными.

Вариант 2 - Если он этого не делает, единственный способ - обработать его на более низком уровне, например, «на метод» или «на небольшой блок кода». Но теперь он должен убедиться, что он начинает транзакцию и совершает/откатывает ее должным образом. Это уменьшает читаемость, сложно пересмотреть код и отладить его в случае возникновения проблем. Кроме того, если это все равно должно быть на более низком уровне, почему бы не включить это в бизнес-логику?

Путь 2] Управляющая транзакция внутри бизнес-логики.

Это делает все сделки недолговечными. Но это не выгодно для дозирования или UnitOfWork. BLL может не знать заранее, как вызовы базы данных будут выполняться при вызове приложения.

Место, где требуется Begin и Commit сделка, которая будет выгодна для дозирования и почитания UnitOfWork?

Edit 1: (После принятия ответа)

Я только что нашел отличную article на UnitOfWork. Ниже приведены некоторые из выдержек из них:

Не используйте антипаттерн для сеанса за сеансом: не открывайте и не закрывайте сеанс для каждого простого вызова базы данных в одном потоке. То же самое верно для транзакций базы данных. Вызов базы данных в приложении производится с использованием запланированной последовательности; они сгруппированы в атомные единицы работы.Это также означает, что автоматическая фиксация после каждого отдельного оператора SQL бесполезна в приложении, так как этот режим предназначен для работы с SQL-консолью ad-hoc.

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

Наиболее распространенный шаблон в многопользовательском клиент-серверном приложении - это сеанс за запрос. В этой модели запрос от клиента отправляется на сервер, где выполняется уровень сохранения Hibernate. Открывается новый сеанс Hibernate, и все операции с базой данных выполняются в этой части работы. По завершении работы, и после того, как был подготовлен ответ для клиента, сеанс очищается и закрывается. Используйте единую транзакцию базы данных для обслуживания запроса клиентов, начиная и фиксируя ее при открытии и закрытии сеанса. Отношения между ними взаимно однозначны, и эта модель идеально подходит для многих приложений.

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

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

+0

Как вы очерчиваете свои варианты использования? Вы используете что-то вроде команд или обработчиков? Они могут формировать естественные границы транзакций. Кроме того, вы можете украсить их, чтобы уменьшить шум. –

+0

также сеанс в форме не имеет большого смысла. Вам действительно нужна единица работы. это легко в веб-среде, потому что запрос является естественной единицей работы. формы там, где это становится немного сложнее, потому что, как @DavidOsborne упоминает, ваши примеры использования/истории должны определять ваши транзакционные границы. – Fran

+0

Также стоит исследовать «сеанс для разговора» и связанные с ним шаблоны. –

ответ

1

Почему бы не предупредить о сделке (услугах)?

что-то вроде

protected virtual TResult Transact<TResult>(Func<TResult> func) 
    { 
     if (_session.Transaction.IsActive) 
      return func.Invoke(); 

     TResult result; 
     using (var tx = _session.BeginTransaction(IsolationLevel.ReadCommitted)) 
     { 
      result = func.Invoke(); 
      tx.Commit(); 
     } 

     return result; 
    } 

    protected virtual void Transact(System.Action action) 
    { 
     Transact(() => 
     { 
      action.Invoke(); 
      return false; 
     }); 
    } 

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

Теперь вы можете скомпоновать несколько вызовов службы под одной транкацией, а не изолировать каждый вызов.

+0

Вместо того, чтобы обслуживать, я решил вместо этого использовать транзакцию Generic Repository. Это дополнительный слой поверх исходного репозитория, но я не вижу другого лучшего решения. Поскольку это общий репозиторий, это меньше кода для изменения и управления. Благодарю. –