NHibernate:我应该在什么范围内使用事务?

时间:2016-12-23 12:18:00

标签: nhibernate transactions data-access-layer unit-of-work

我正在使用NHibernate开发数据访问层。它将用于我的业务逻辑层。我的应用程序是多个其他应用程序(ASP.NET MVC,Windows服务,Windows窗体,ASP.NET Web API)的集合,这些应用程序将使用相同的业务逻辑层。业务逻辑层将访问数据访问层。应用程序不会直接访问数据访问层。

我知道我不应该依赖隐式事务,并且应该在显式事务中包含所有数据库调用(包括READ调用)。

据我了解,交易必须是短暂的。如果它存在很长时间,可能会产生问题。请参阅12

许多资源(在StackOverflow和其他网站上)建议在using块中包含事务代码。这确实很有意义,因为它有助于为每个数据库调用使用显式事务。它还有助于使交易寿命缩短。这种方法的问题在于,批处理仅限于using块。 UnitOfWork未得到充分利用。

另一方面,许多资源(在StackOverflow和其他站点上)建议在Web应用程序中使用某些更高级别的事务,例如web-request(每个请求的会话),或者按照窗体形式使用事务处理#34;在WinForms等中。这改进了查询的批处理,并有助于更好地利用UnitOfWork模式。这种方法的问题在于,交易是长期存在的。

方式1]将事务公开给应用程序并让它处理它。

选项1 - 来电者可以在请求级别处理此问题。这种方法的缺点是,交易可能很长寿。

选项2 - 如果他不这样做,唯一的方法是在较低级别处理它,例如"每种方法"或者"每小块代码"。但是现在,他必须确保他开始事务并正确地提交/回滚它。这降低了可读性,在出现问题时难以进行代码审查和调试。此外,如果这必须处于较低级别,为什么不将其包含在业务逻辑层中呢?

方式2]控制业务逻辑层内的交易。

这使得所有交易都很短暂。但这并不能很好地利用批处理或UnitOfWork。 BLL可能不会事先知道如何通过调用应用程序来进行数据库调用。

BeginCommit交易的推荐位置是什么,可以充分利用批量处理和尊重UnitOfWork

编辑1 :(接受答案后)

我刚在UnitOfWork找到了很好的article。以下是一些相同的摘录:

  

不要使用session-per-operation反模式:不要在单个线程中为每个简单数据库调用打开和关闭Session。数据库事务也是如此。应用程序中的数据库调用使用计划的序列进行;它们被分为原子工作单元。这也意味着每个SQL语句在应用程序中无效后自动提交,因为此模式用于临时SQL控制台工作。

     

数据库事务永远不是可选的。与数据库的所有通信都必须在事务内部进行。应该避免读取数据的自动提交行为,因为许多小事务不可能比一个明确定义的工作单元执行得更好。后者也更易于维护和扩展。

     

多用户客户端/服务器应用程序中最常见的模式是每个请求的会话。在此模型中,来自客户端的请求被发送到运行Hibernate持久层的服务器。打开一个新的Hibernate会话,所有数据库操作都在这个工作单元中执行。完成工作后,一旦准备好客户的响应,就会刷新并关闭会话。使用单个数据库事务来为客户端请求提供服务,在打开和关闭会话时启动并提交它。两者之间的关系是一对一的,这个模型非常适合许多应用。

     

每个请求的会话模式不是设计工作单元的唯一方法。许多业务流程需要与用户进行一系列与数据库访问交错的交互。在Web和企业应用程序中,数据库事务跨越用户交互是不可接受的。

     

数据库或系统,事务边界始终是必需的。在数据库事务之外不会发生与数据库的通信(这似乎使许多习惯于自动提交模式的开发人员感到困惑)。始终使用明确的事务边界,即使对于只读操作也是如此。根据您的隔离级别和数据库功能,这可能不是必需的,但如果您始终明确划分事务,则没有任何缺点。当然,即使是读取数据,单个数据库事务也会比许多小事务执行得更好。

1 个答案:

答案 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;
        });
    }

在您的服务或仓库中检查交易是否处于活动状态并参与活动交易或如果不存在则创建新交易。

现在,您可以在一次交易中组合多个服务呼叫,而不是孤立地进行每次呼叫。