15

我有以下实现,并希望得到一些关于它是否正确使用 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();

下面是一个用法示例:

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'。”

4

3 回答 3

35

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

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

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

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

于 2010-11-24T20:57:53.947 回答
8

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

这篇优秀文章中的更多信息。

所以我最终得到的是:

  • IUnitOfWork-> 包装会话,实现IDisposable
  • IAtomicUnitOfWork-> 包装交易,实现IDisposable
  • IRepository-> 提供获取、保存、删除和查询访问

我这样做是为了让你需要一个IUnitOfWork来构建一个IAtomicUnitOfWork并且你需要一个IAtomicUnitOfWork来构建一个IRepository,这样就可以强制执行适当的事务管理。这就是我通过实现自己的接口所获得的一切。

正如 jeroenh 所说,您几乎可以很好地使用ISessionITransaction但最后我觉得针对我定义的接口编写所有代码要好一些。

于 2010-11-25T01:54:13.577 回答
4

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

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/

希望这可以帮助。

鲍勃

于 2010-11-25T16:12:06.540 回答