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

正确使用NHibernate工作单元模式和Ninject,nhibernate,session,transactions,ninject,unit-of-work,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 _se

我有以下实现,并希望得到一些反馈,以了解它是否正确地将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绑定

Bind<IUnitOfWork>().To<UnitOfWork>().InTransientScope();
Bind<ISessionFactory>().ToProvider<NHibernateSessionFactoryProvider>().InSingletonScope();
Bind<IRepository>().To<Repository>().InTransientScope();
在我以前的实现中,我会像这样将IUnitOfWork注入到我的存储库构造函数中

public Repository(IUnitOfWork unitOfWork)
    {...

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

第一个观察:存储库不应提交工作单元。这违背了工作单元模式的全部要点。通过立即将更改保存到存储库中,您可以“微观管理”NHibernate会话

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

UnitOfWork类本身看起来不错,不过您应该问问自己是否真的需要它。在NHibernate中,会话是您的工作单元。UnitOfWork类似乎没有增加太多的值(特别是因为您仍然公开了CurrentSession属性)


但你需要考虑它的一生。我认为你在这一点上错了。会话生存期管理取决于您正在开发的应用程序的类型:在web应用程序中,您通常希望每个请求有一个工作单元(您可能希望谷歌搜索“每个请求的nhibernate会话”)。在桌面应用程序中,它稍微复杂一些,大多数情况下,您需要“每个屏幕的会话”或“每个业务事务的会话”。

我有一个大部分是CRUD类型的应用程序,我使用存储库模式实现了工作单元,但无法真正摆脱会话/事务分离。会话和事务需要不同的生存期。在桌面世界中,会话通常是“每个屏幕”,事务是“每个用户操作”

更多信息请参阅本节

所以我最终得到的是:

  • IUnitOfWork
    ->包装会话,实现
    IDisposable
  • IAtomicUnitOfWork
    ->包装事务,实现
    IDisposable
  • IRepository
    ->提供获取、保存、删除和查询访问
我这样做是为了让您需要一个
IUnitOfWork
来构建一个
IAtomicUnitOfWork
,并且您需要一个
IAtomicUnitOfWork
来构建一个
IRepository
,以便实施适当的事务管理。这就是我通过实现自己的接口所获得的一切


正如jeroenh所说,您几乎可以使用
ISession
ITransaction
,但最终,我觉得根据我定义的接口编写所有代码要好一点。

答案的一个重要部分在于您希望事务大小是什么。现在(正如jeroenh所指出的),事务是存储库中每个方法调用的事务。这是非常小的,可能不需要。我创建了一个ASP.MVC应用程序,它使用的事务大小包含了从单个http请求到所有内容。这可能是多个数据库读取/更新。我为国际奥委会使用相同的工作单位和项目。看一看,也许有些东西能帮你解决问题:

希望这有帮助


鲍勃

感谢杰罗恩的反馈。我们的应用程序只需要执行简单的crud操作,而不需要执行多个操作并同时提交所有内容。我之所以选择不直接针对ISession工作,是因为我希望UnitOfWork将开始的会话、开始的会话和结束的会话抽象出来。IUnitOfWork的好处是减少了与ORM的耦合。或者至少这是我能想到的唯一好处:)@Ryan Barett但这有什么用呢?在实用主义和理想主义之间的永恒斗争中又打了一轮,我现在更倾向于艾伦德的观点……我不喜欢艾伦德用“绝对主义”来表达他的一些观点,包括这一点。它是否能为抽象出ORM增加价值取决于应用程序的总体复杂性。如果您的应用程序只执行简单的CRUD,那么它实际上只会增加不必要的复杂性。然而,如果您正在进行全面的DDD(并且您的项目的复杂性实际上保证了DDD),那么将ORM抽象到存储库、UoW等后面可能会增加价值。推荐阅读:“领域驱动设计的模式、原则和实践”(Millet&Tune)。感谢您提供的资源。尽管我的应用程序不是MVC应用程序,但想法是一样的。嗨,鲍勃,不幸的是,你的博客的url已从blog.bobcravens.com更改为bobcravens.com。因此很多东西都被破坏了,比如图像链接。希望你能把它修好。把IUoW发送到IRepository是个好主意。就概念上的拆分而言:IUoW根据定义是一个事务。会话的概念有点不同,它上面的抽象应该有一个不同的名称(我称之为IDatabaseGenericSession)@Ryan Barrett:是的,我认为会话包装器的更好名称应该是某种形式的“上下文”。我认为ObjectContext已经被.NET接受了,不是吗?可能是EntityContext?不确定您的最终决定是什么,但不要这样做!:)说真的,NHibernate已经添加了大量抽象和UoW模式。只需直接使用NHibernate,并保持代码简单。你真的想要更多的抽象来调试吗?
public Repository(IUnitOfWork unitOfWork)
    {...