我有以下实现,并想知道是否正确使用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'。”
答案 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所说,你几乎可以使用ISession
和ITransaction
,但最后我觉得在我定义的界面上编写所有代码要好一些。
答案 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/
希望这有帮助。
鲍勃