使用 Repositories/Transactions 时如何管理 NHibernate 会话

How to manage NHibernate sessions when using Repositories/Transactions

我现在陷入了 NHibernate 的困境,我正在努力找出如何最好地正确设计我的存储库,同时正确管理会话的生命周期。

从我看到的示例来看,将 ISession 注入每个存储库似乎是常见的做法,如下所示:

public class SomeRepository : IRepository
{
    private readonly ISession _session;

    public SomeRepository(ISession session)
    {
        _session = session;
    }

    public IList<T> LoadSomeData()
    {
        return _session.Query<T>().Where(...).ToList();
    }
}

这很好,但我可以看到一些会出现问题的情况:

1) 在应用程序的生命周期内可能没有 1 个会话。

2) 当访问 NHibernate 时,我们应该始终将我们的调用包装在事务中——如果事务出错,那么我们必须回滚事务并关闭会话。

在这两种情况下,存储库都将失效,因为它引用了一个已关闭的会话。

我的特定应用程序是一个很长的 运行 进程,它只会偶尔调用 NHibernate,因此我希望会话的周转率很高,而不是在应用程序的整个生命周期中保持 1 个会话打开。

因此我想知道处理这种特殊情况的既定模式是什么?我看到了几个潜在的解决方案,但很难看出哪个是最佳实践:

1) 每次进程需要做一些数据库工作时重建存储库(并创建一个新会话)。

2) 使用 SessionFactory 注入存储库。 Repository 然后公开此 SessionFactory。每个存储库的消费者然后在开始事务的同时打开一个新会话。

3) 创建一个 UnitOfWork class,它被注入到存储库中并负责管理会话生命周期和事务。每次调用 Repository 时,都会调用 UnitOfWork,这会创建一个全新的 Session 并在 Transaction 中执行调用。因此,存储库不知道 Sessions/Transactions.

对我来说,3) 似乎是最好的解决方案,我在网上看到了几个这样的例子,但在所有的例子中,他们只在 UnitOfWork 中创建 1 个会话,如果事务被回滚。

此外,3) 的一个限制是 UnitOfWork 绑定到特定的存储库,因此您不能拥有调用不同存储库的事务。

希望这是有道理的,并希望得到任何指导。

谢谢

其实NHibernate中的Session可以作为UnitOfWork。但是如果你想使用这个模式,这里是实现:http://nhibernate.info/doc/patternsandpractices/nhibernate-and-the-unit-of-work-pattern.html.

但是,您列表中的最佳解决方案是 1。因此,每次需要使用 DB 进行作业时,您都需要创建会话和存储库。

使用 UnitOfWork 看起来像:

using(var uow = new UnitOfWork())
{
   var rep1 = new SomeRepository1(uow);
   rep1.DoSomeJob();

   var rep2 = new SomeRepository2(uow);
   rep2.DoSomeOtherJob();
}

使用本机 ISession:

using(var session = SessionFactory.OpenSession())
using(var tr = session.BeginTransaction())
{
   var rep1 = new SomeRepository1(session);
   rep1.DoSomeJob();

   var rep2 = new SomeRepository2(session);
   rep2.DoSomeOtherJob();

   tr.Commit();
}

您还可以使用 DI/IoC 容器的强大功能来管理会话生命周期。

如果你对最后一个选项感兴趣,我可以给你举个例子。