C# ninject将iunitofwork注入到存储库范围的属性

C# ninject将iunitofwork注入到存储库范围的属性,c#,nhibernate,ninject,unit-of-work,irepository,C#,Nhibernate,Ninject,Unit Of Work,Irepository,让我从我目前的设置开始,然后解释我试图实现的目标。我们正在使用NHibernate,并试图用Ninject实现IRepository/IUnitOfWork模式。理想情况下,无论是ASP.Net、WCF还是其他什么应用程序,它都应该通用地工作 工夫 公共接口IUnitOfWork { object Add(object obj);//我们要公开的所有其他受支持的CRUD操作 无效提交(); 无效回滚(); } 工作单元 公共类UnitOfWork:IUnitOfWork { 私有只读ISess

让我从我目前的设置开始,然后解释我试图实现的目标。我们正在使用NHibernate,并试图用Ninject实现IRepository/IUnitOfWork模式。理想情况下,无论是ASP.Net、WCF还是其他什么应用程序,它都应该通用地工作

工夫

公共接口IUnitOfWork
{
object Add(object obj);//我们要公开的所有其他受支持的CRUD操作
无效提交();
无效回滚();
}
工作单元

公共类UnitOfWork:IUnitOfWork
{
私有只读ISessionFactory _sessionFactory;
专用只读会话;
私有只读ITransaction\u事务;
公共工作单元(ISessionFactory sessionFactory)
{
_sessionFactory=sessionFactory;
_会话=_sessionFactory.OpenSession();
_事务=_session.BeginTransaction();
}
公共对象添加(对象obj)
{
返回会话保存(obj);
}
公共无效提交()
{
如果(!\u transaction.IsActive)
{抛出新异常(“某些错误”);}
_Commit();
}
公共无效回滚()
{
如果(!\u transaction.IsActive)
{
抛出新异常(“其他错误”);
}
_transaction.Rollback();
}
}
间接的

公共接口假定,其中tenty:class
{
TId Add(TEntity项);//添加其他缺少的CRUD操作
}
一般保存

公共类GenericRepository:IRepository
地点:班级
{
公共TId添加(临时项目)
{
抛出新的NotImplementedException();
}
}
我正在使用Ninject作为IOC容器。目标是在创建UnitOfWork的生命周期中重用相同的IUnitOfWork。我希望实现的生命周期能够正常工作,无论调用的应用程序是什么,否则我会像大多数在线建议一样在RequestScope中使用。 我可以做这样的事情:

//构造函数
公共MyService(IUnitOfWork uow、IRepository userRepo、IRepository catRepo)
{
_uow=uow;_userRepo=userRepo;_catRepo=catRepo;
}
//方法在同一类中
公共工程
{
_userRepo.Add(someUser);
_catRepo.Add(someCat);
_提交();
//错误回滚
}
我的绑定设置如下:

Bind.To().InCallScope();
绑定(typeof(IRepository))到(typeof(genericpository));
这个绑定配置实际上适用于上面的
MyService
,它将在构造函数中创建一次UnitOfWork,并且它也将对IRepo impl使用相同的UnitOfWork,不管它们到底有多少层

但我希望能够做的是将IUnitOfWork完全隐藏在应用程序之外。我宁愿提供一些可以放在方法顶部的TransactionAttribute,它将在条目上创建IUnitOfWork,并且相同的实例将被注入到TransactionAttribute范围内所有未来的IUnitOfWork请求中。它将负责相应地提交和回滚。所以前面的代码会变成这样:

//构造函数
公共MyService(IRepository userRepo、IRepository catRepo)
{
_uow=uow;_userRepo=userRepo;_catRepo=catRepo;
}
//方法在同一类中
[交易]
公共工程
{
_userRepo.Add(someUser);
_catRepo.Add(someCat);
}

是否有任何类型的绑定设置可以让我用这样的[Transaction]标记一个方法?我愿意对IUnitOfWork和IRepository的内容进行一些小的重组,而服务层代码只是废弃代码,因此我可以在那里非常灵活。

首先,注入将无法使用

[Transaction]
public void DoSomeWork()
{
      _userRepo.Add(someUser);
      _catRepo.Add(someCat);
}
毕竟,容器不知道要调用什么样的方法以及何时调用它

更进一步,因为你想让它与WebApps、WCF一起工作,。。也许是石英石的工作,或者不是:你必须决定是否要将
IUnitOfWork
/
存储库的生命周期
/NHibernate
会话
与使用它的对象的生命周期(如
MyService
)联系起来,或者是否希望服务的寿命更长(例如单例)并为如下方法调用创建新的NHibernate
会话

[Transaction]
public void DoSomeWork()
{
    _userRepo.Add(someUser);
    _catRepo.Add(someCat);
}
这实际上可能通过使用或来实现。或者,您也可以考虑使用decorator模式来实现这一点(参见示例)。然而,如果我没记错的话,ninject目前缺少一些细节来支持easz装饰器处理

除了
[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
}
由于需要在其他对象中访问nhibernate
会话
,例如
\u userRepo
\u catRepo
,因此还需要一种独立于容器访问
会话的方法,因为它与实例化对象的方式/时间无关。
当然,你可以这样传递:

public void DoSomeWork()
{
    using(IUnitOfWorkSession session = _unitOfWork.Begin())
    {    
        _userRepo.Add(session, someUser);
        _catRepo.Add(session, someCat);
        session.Commit();
    }
}
但这并不酷。因此,您需要使用类似于
ThreadLocal
(如果使用async/await,这可能会有问题)或
SynchronizationContext
-本地存储的东西

现在,如果你已经成功做到这一点,你可以考虑做AOP。 AOP将有两种方法来处理两件事:

  • 访问IUnitOfWork。
    • 可以通过将其编织为附加参数来实现
    • 或者它可以检查是否已经有一个,如果有,它可以使用这个,如果没有,它可以抛出或编织的论点在
  • 使用与上面相同的代码包装装饰的方法,如:

我们目前使用的是
PostSharp
(v2.1)
public void DoSomeWork()
{
    using(IUnitOfWorkSession session = _unitOfWork.Begin()) // weave this
    {    
        _userRepo.Add(session, someUser);
        _catRepo.Add(session, someCat);
        session.Commit(); // weave this
    }
}