让我从目前的设置开始,然后解释我想要实现的目标。我们正在使用NHibernate并尝试使用Ninject实现IRepository / IUnitOfWork模式。理想情况下,对于使用代码的任何应用程序,无论是ASP.Net,WCF还是其他应用程序,它都应该通用。
IUnitOfWork
public interface IUnitOfWork
{
object Add(object obj);//all other supported CRUD operations we want to expose
void Commit();
void Rollback();
}
的UnitOfWork
public class UnitOfWork : IUnitOfWork
{
private readonly ISessionFactory _sessionFactory;
private readonly ISession _session;
private readonly ITransaction _transaction;
public UnitOfWork(ISessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
_session = _sessionFactory.OpenSession();
_transaction = _session.BeginTransaction();
}
public object Add(object obj)
{
return _session.Save(obj);
}
public void Commit()
{
if(!_transaction.IsActive)
{throw new Exception("some error");}
_transaction.Commit();
}
public void Rollback()
{
if (!_transaction.IsActive)
{
throw new Exception("some other error");
}
_transaction.Rollback();
}
}
IRepository
public interface IRepository<TEntity, TId> where TEntity : class
{
TId Add(TEntity item);//add other missing CRUD operations
}
GenericRepository
public class GenericRepository<TEntity, TId> : IRepository<TEntity, TId>
where TEntity : class
{
public TId Add(TEntity item)
{
throw new NotImplementedException();
}
}
我使用Ninject作为我的IOC容器。目标是在创建UnitOfWork的生命周期中重用相同的IUnitOfWork。无论调用应用程序是什么,我都希望实现的生命周期能够工作,否则我会像在线大多数建议一样使用InRequestScope。 我能够做到这样的事情:
//constructor
public MyService(IUnitOfWork uow, IRepository<User, int> userRepo, IRepository<Cat, int> catRepo)
{
_uow = uow; _userRepo = userRepo; _catRepo = catRepo;
}
//method in same class
public void DoSomeWork()
{
_userRepo.Add(someUser);
_catRepo.Add(someCat);
_uow.Commit();
//rollback on error
}
我的绑定设置如下:
Bind<IUnitOfWork>.To<UnitOfWork>().InCallScope();
Bind(typeof(IRepository<,>)).To(typeof(GenericRepository<,>));
这个绑定配置实际上适用于上面的MyService
,它将在构造函数中创建一次UnitOfWork,并且它将同样的UnitOfWork用于IRepo impls,无论它们实际上有多少层是
但我希望能够完全隐藏IUnitOfWork远离应用程序。我宁愿提供一些可以置于方法之上的TransactionAttribute,它将在条目上创建IUnitOfWork,并且将在TransactionAttribute范围内的所有任何IUnitOfWork请求中注入相同的实例。它会照顾到相应的提交和回滚。所以前面的代码会变成这样:
//constructor
public MyService(IRepository<User, int> userRepo, IRepository<Cat, int> catRepo)
{
_uow = uow; _userRepo = userRepo; _catRepo = catRepo;
}
//method in same class
[Transaction]
public void DoSomeWork()
{
_userRepo.Add(someUser);
_catRepo.Add(someCat);
}
我可以做任何类型的绑定设置,这样我就能用这样的[Transaction]标记方法吗?我对IUnitOfWork和IRepository的一些小的重组是开放的,而服务层代码只是废弃代码,因此我可以非常灵活。
答案 0 :(得分:0)
首先,注入不适用于
[Transaction]
public void DoSomeWork()
{
_userRepo.Add(someUser);
_catRepo.Add(someCat);
}
毕竟,容器无法知道你要打电话给你的方法,以及何时打电话。
更进一步,因为你希望它可以与WebApps,WCF一起使用,也许是一些石英工作或者不是:你必须决定是否要将IUnitOfWork
/ {的生命周期联系起来{1}} / NHibernate Repository
到使用它的对象的生命周期(如Session
),或者如果你想让服务更长寿(例如单身)并创建一个新的NHibernate {{ 1}}用于方法调用,如:
MyService
这实际上可能是AOP使用Fody或PostSharp实现的。或者,您也可以考虑使用装饰器模式来实现此目的(参见例如here)。但是,如果我没记错的话,ninject目前缺少支持easz装饰器处理的一些细节。
除Session
属性外,您可以从明确控制所有内容开始:
[Transaction]
public void DoSomeWork()
{
_userRepo.Add(someUser);
_catRepo.Add(someCat);
}
由于您需要访问其他对象中的nhibernate [Transaction]
,例如public interface IUnitOfWork
{
IUnitOfWorkSession Begin(); // starts a session and transaction
}
public interface IUnitOfWorkSession : IDisposable
{
void Commit();
void Dispose(); // performs rollback in case no commit was performed
}
和Session
,您还需要一种方法来访问_userRepo
独立的对象容器,因为它与实例化对象的方式/时间无关。
当然,你可以传递它:
_catRepo
但那并不酷。因此,您需要使用像Session
这样的东西(如果你使用async / await这可能会有问题)或者public void DoSomeWork()
{
using(IUnitOfWorkSession session = _unitOfWork.Begin())
{
_userRepo.Add(session, someUser);
_catRepo.Add(session, someCat);
session.Commit();
}
}
- 本地存储。
现在,如果你已经成功实现这一目标,你可以考虑做AOP。 AOP有两个处理两件事:
ThreadLocal