正确使用NHibernate工作单元模式和Ninject

时间:2010-11-24 20:32:24

标签: nhibernate session transactions ninject unit-of-work

我有以下实现,并想知道是否正确使用NHibernate进行会话和事务。

public interface IUnitOfWork : IDisposable
{
    ISession CurrentSession { get; }
    void Commit();
    void Rollback();
}

public class UnitOfWork : IUnitOfWork
{
    private readonly ISessionFactory _sessionFactory;
    private readonly ITransaction _transaction;

    public UnitOfWork(ISessionFactory sessionFactory)
    {
        _sessionFactory = sessionFactory;
        CurrentSession = _sessionFactory.OpenSession();
        _transaction = CurrentSession.BeginTransaction();
    }

    public ISession CurrentSession { get; private set; }

    public void Dispose()
    {
        CurrentSession.Close();
        CurrentSession = null;
    }

    public void Commit()
    {
        _transaction.Commit();
    }

    public void Rollback()
    {
        if (_transaction.IsActive) _transaction.Rollback();
    }
}

Ninject binding

Bind<IUnitOfWork>().To<UnitOfWork>().InTransientScope();
Bind<ISessionFactory>().ToProvider<NHibernateSessionFactoryProvider>().InSingletonScope();
Bind<IRepository>().To<Repository>().InTransientScope();

以下是用法示例:

public class Repository : IRepository
{
    private readonly ISessionFactory _sessionFactory;

    public Repository(ISessionFactory sessionFactory)
    {
        _sessionFactory = sessionFactory;
    }

    public void Add(IObj obj)
    {
        using (var unitOfWork = new UnitOfWork(_sessionFactory))
        {
            unitOfWork.CurrentSession.Save(obj);
            unitOfWork.Commit();
        }         
    }
}

在我之前的实现中,我会将IUnitOfWork注入到我的存储库构造函数中,如此

public Repository(IUnitOfWork unitOfWork)
    {...

但Dispose()方法不会执行,导致后续调用抛出此异常:“无法访问已处置的对象。对象名称:'AdoTransaction'。”

3 个答案:

答案 0 :(得分:35)

首先观察:您的存储库不应该提交工作单元。这打破了工作单元模式的全部要点。通过立即将更改保存在存储库中,您可以“微观管理”NHibernate会话。

工作单元应在应用程序/服务层的堆栈中更高的位置引用。这允许您拥有执行多个操作的应用程序代码,可能在不同的存储库上,并且最后仍然可以一次提交所有内容。

UnitOfWork类本身看起来很好,但你应该问自己是否真的需要它。在NHibernate中,ISession是您的工作单元。你的UnitOfWork类似乎没有增加很多价值(特别是因为你无论如何暴露了CurrentSession属性)

但你需要考虑它的寿命。我认为你在这一点上做错了。会话生命周期管理取决于您正在开发的应用程序类型:在Web应用程序中,您通常希望每个请求都有一个工作单元(您可能希望在每个请求的'nhibernate会话'上进行google)。在桌面应用程序中,它稍微复杂一点,您大多数时候都想要“每个屏幕的会话”或“每个业务交易的对话”。

答案 1 :(得分:8)

我有一个主要是CRUD类型的应用程序,我实现了Unit of Work with Repository模式,但是无法真正摆脱Session / Transaction分裂。会话和交易需要不同的生命周期。在桌面世界中,会话通常是“按屏幕”,而事务是“按用户操作”。

excellent article中的更多信息。

所以我最终得到的是:

  • IUnitOfWork - &gt;包装会话,实现IDisposable
  • IAtomicUnitOfWork - &gt;包装事务,实现IDisposable
  • IRepository - &gt;提供获取,保存,删除和查询访问权限

我这样做是为了让您需要IUnitOfWork来构建IAtomicUnitOfWork,并且您需要IAtomicUnitOfWork来构建IRepository,以便执行正确的事务管理。这实际上是我通过实现自己的接口获得的。

正如jeroenh所说,你几乎可以使用ISessionITransaction,但最后我觉得在我定义的界面上编写所有代码要好一些。

答案 2 :(得分:3)

答案的一个重要部分在于您希望自己的交易规模。现在(正如jeroenh所指出的),事务是在您的存储库上按方法调用。这非常小,可能不需要。我创建了一个ASP.MVC应用程序,它使用的事务大小包含来自单个http请求的所有内容。这可能是多个数据库读取/更新。我正在使用相同的工作单元和Ninject for IOC。看看,也许有些东西可以帮助解决您的问题:

http://bobcravens.com/2010/06/the-repository-pattern-with-linq-to-fluent-nhibernate-and-mysql/

http://bobcravens.com/2010/07/using-nhibernate-in-asp-net-mvc/

http://bobcravens.com/2010/09/the-repository-pattern-part-2/

http://bobcravens.com/2010/11/using-ninject-to-manage-critical-resources/

希望这有帮助。

鲍勃