ninject将iunitofwork注入存储库作用域属性

时间:2014-10-30 23:46:55

标签: c# nhibernate ninject unit-of-work irepository

让我从目前的设置开始,然后解释我想要实现的目标。我们正在使用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的一些小的重组是开放的,而服务层代码只是废弃代码,因此我可以非常灵活。

1 个答案:

答案 0 :(得分:0)

首先,注入不适用于

[Transaction]
public void DoSomeWork()
{
      _userRepo.Add(someUser);
      _catRepo.Add(someCat);
}

毕竟,容器无法知道你要打电话给你的方法,以及何时打电话。

更进一步,因为你希望它可以与WebApps,WCF一起使用,也许是一些石英工作或者不是:你必须决定是否要将IUnitOfWork / {的生命周期联系起来{1}} / NHibernate Repository到使用它的对象的生命周期(如Session),或者如果你想让服务更长寿(例如单身)并创建一个新的NHibernate {{ 1}}用于方法调用,如:

MyService

这实际上可能是AOP使用FodyPostSharp实现的。或者,您也可以考虑使用装饰器模式来实现此目的(参见例如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有两个处理两件事:

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