ninject注入iunitofwork到存储库作用域属性 [英] ninject inject iunitofwork to repository scoped attribute

查看:773
本文介绍了ninject注入iunitofwork到存储库作用域属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我开始与我目前的设置,然后解释什么,我想要的目的。我们正在使用NHibernate和努力来实现与Ninject的IRepository / IUnitOfWork格局。它应该理想地要求实现任何应用程序正在使用的代码工作,是ASP.Net,WCF或别的东西。



IUnitOfWork



<预类=郎-CS prettyprint-覆盖> 公共接口IUnitOfWork
{
对象添加(obj对象); //我们希望所有其他支持的CRUD操作揭露
无效提交();
无效回滚();
}



的UnitOfWork



<预类=郎-CS prettyprint-覆盖> 公共类的UnitOfWork:IUnitOfWork
{
私人只读ISessionFactory _sessionFactory;
私人只读的Isession _session;
私人只读ITransaction _transaction;

公众的UnitOfWork(ISessionFactory SessionFactory的)
{
_sessionFactory = SessionFactory的;
_session = _sessionFactory.OpenSession();
_transaction = _session.BeginTransaction();
}

公共对象添加(obj对象)
{
返回_session.Save(OBJ);
}

公共无效提交()
{
如果(_ transaction.IsActive!)
{抛出新的异常(某些错误);}
_transaction.Commit();
}

公共无效回滚()
{
如果(!_transaction.IsActive)
{
抛出新的异常(其他一些错误);
}
_transaction.Rollback();
}
}



IRepository



<预类=郎-CS prettyprint-覆盖> 公共接口IRepository< TEntity,TID>其中,TEntity:类
{
TID添加(TEntity项目); //添加其他失踪CRUD操作
}

GenericRepository



<预类=郎-CS prettyprint-覆盖> 公共类GenericRepository< TEntity,TID> ; :IRepository< TEntity,TID>
,其中TEntity:类
{
公共TID添加(TEntity项目)
{
抛出新NotImplementedException();
}
}



我使用Ninject作为我的IOC容器。的目标是重用为的UnitOfWork的创建的生命周期相同IUnitOfWork。我想这是实施工作的无论什么调用应用程序,否则我会只用InRequestScope最喜欢的网上建议的生命周期。
我能够做这样的事:



<预类=郎-CS prettyprint-覆盖> //构造
酒店的公共则将MyService(IUnitOfWork UOW,IRepository<使用者,INT> userRepo,IRepository<猫,INT> catRepo)
{
_uow = UOW; _userRepo = userRepo; _catRepo = catRepo;
}

//在同一类
公共无效DoSomeWork()
{
_userRepo.Add(SomeUser的)方法;
_catRepo.Add(someCat);
_uow.Commit();对错误
}


//回滚

和我绑定设置了这样的:



<预类=郎-CS prettyprint-覆盖> 绑定< IUnitOfWork>。为<的UnitOfWork>()InCallScope( );
绑定(typeof运算(IRepository<,>))。为了(typeof运算(GenericRepository<,>));

和此绑定配置的实际工作为上述为MyService ,它会在构造函数中创建一次的UnitOfWork,它将用于IRepo impls相同的UnitOfWork为好,不管他们可能多少层下来实际上是。



但我希望能够做的就是隐藏IUnitOfWork从应用程序完全离开。我宁愿提供一些TransactionAttribute可放置在一个方法的顶部,这将在条目创建IUnitOfWork和相同的实例将被注入到用于TransactionAttribute的范围之内的IUnitOfWork所有未来的任何请求。而这将需要提交的照顾和相应rollingback。所以上面的代码会变成这样的:



<预类=郎-CS prettyprint-覆盖> //构造
公众为MyService (IRepository<使用者,INT> userRepo,IRepository<猫,INT> catRepo)
{
_uow = UOW; _userRepo = userRepo; _catRepo = catRepo;
}

//在同一类
法[交易]
公共无效DoSomeWork()
{
_userRepo.Add(SomeUser的) ;
_catRepo.Add(someCat);
}

是否有任何形式的结合设置我能做到这将使我来标记用[交易]这样的方法?我打开的IUnitOfWork和IRepository东西,一些小的调整,和业务层代码仅仅是取消代码,所以我可以很灵活那里。


解决方案

首先,注射不会与工作

  [交易] 
公共无效DoSomeWork( )
{
_userRepo.Add(SomeUser的);
_catRepo.Add(someCat);
}



毕竟,容器不知道你是什么样的方法要叫,当你要调用它。



走一步进一步,因为你希望它具有的WebApp,WCF,工作..也许一些石英工作或没有什么:你必须决定是否要扎一个 IUnitOfWork / / NHibernate的<生命周期code>会话到使用它的对象的生命周期(如为MyService ),或者如果你想有一个服务寿命更长(对于例如一个单),并创建一个新的NHibernate 会话的方法调用,如:

  [交易] 
公共无效DoSomeWork()
{
_userRepo.Add(SomeUser的);
_catRepo.Add(someCat);
}

这实际上可能可能会被通过的 AOP ,或者使用 Fody PostSharp 。另外,您也可以考虑使用Decorator模式实现这一目标(例如见的这里)。但是,如果我没有记错ninject目前缺乏一些细微的支持easz装饰处理。



另外在 [交易] 属性,你可以用明确地控制一切启动:

 公共接口IUnitOfWork 
{
IUnitOfWorkSession开始( ); //启动会话和交易
}

公共接口IUnitOfWorkSession:IDisposable的
{
无效提交();
无效的Dispose(); //如果执行回滚没有提交进行
}

由于您需要访问NHibernate的会话中的其他对象,如 _userRepo _catRepo 您还需要一种方法来访问会话有独立于容器,因为它什么都没有做与如何/当你实例化对象。
。当然,你可以把它传递这样的:

 公共无效DoSomeWork()
{
使用(IUnitOfWorkSession会话= _unitOfWork.Begin())
{
_userRepo.Add(会话,SomeUser的);
_catRepo.Add(会话,someCat);
session.Commit();
}
}



但是,这不是真的很酷。因此,你需要使用像一个的ThreadLocal (如果你使用异步/等待,这可能是有问题的)或其他的SynchronizationContext -local存储。



现在,如果你已经成功地走到这一步,你可以考虑做AOP。
AOP将有两个处理两件事情:




  • 获得访问IUnitOfWork。

    • 可以通过编织它作为额外的构造函数参数

    • 做,或者可以检查是否已经有一个,如果有,它可以用这一个,如果不是,就既可以抛出或编织论点...


  • 包装装饰方法就像用相同的代码如上:





 公共无效DoSomeWork() 
{
使用(IUnitOfWorkSession会话= _unitOfWork.Begin())//编织这个
{
_userRepo.Add(会话,SomeUser的);
_catRepo.Add(会话,someCat);
session.Commit(); //编织这种
}
}


Let me start with my current setup, and then explain what I am trying to achieve. We are using NHibernate and trying to implement the IRepository/IUnitOfWork pattern with Ninject. It should ideally work generically for whatever application is using the code, whether is ASP.Net, WCF or something else.

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();
    }
}

I am using Ninject as my IOC container. The goal is to reuse the same IUnitOfWork for the life cycle of the creating of the UnitOfWork. I want the life cycle that is implemented to work no matter what the calling application is, or else I would have just used InRequestScope like most suggestions online. I was able to do something like this:

//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
}

And my bindings are set up like:

Bind<IUnitOfWork>.To<UnitOfWork>().InCallScope();
Bind(typeof(IRepository<,>)).To(typeof(GenericRepository<,>));

And this binding configuration actually works for the above MyService, it will create the UnitOfWork once in the constructor and it will use that same UnitOfWork for the IRepo impls as well, no matter how many layers down they may actually be.

But what I would like to be able to do is hide the IUnitOfWork away from the applications altogether. I would rather provide some TransactionAttribute that can be placed on top of a method and it will create the IUnitOfWork on entry and that same instance will be injected to all any future requests for an IUnitOfWork within the scope of the TransactionAttribute. And it would take care of committing and rollingback accordingly. So the previous code would become something like this:

//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);
}

Is there any kind of binding setup I can do that will enable me to mark a method with a [Transaction] like this? I am open to some minor restructuring of the IUnitOfWork and IRepository stuff, and the Service layer code is just scrap code so I can be very flexible there.

解决方案

First of, injecting won't work with

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

after all, the container can't know what kind of method you're going to call and when you're going to call it.

Taking things further, since you want it to work with WebApps, WCF,.. maybe some quartz job or what not: You'll have to decide whether you want to tie the lifetime of an IUnitOfWork / Repository / NHibernate Session to the lifetime of the objects which use it (like MyService) or if you want a service to live longer (for example a singleton) and create a new NHibernate Session for for a method call like:

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

This could actually probably be achieved by AOP, either using Fody or PostSharp. Alternatively you could also look into using the decorator pattern to achieve this (see for example here). However if i remember correctly ninject currently lacks some of the niceties to support easz decorator handling.

Alternatively to the [Transaction] attribute you could start with controlling everything explicitly:

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
}

Since you will need to access the nhibernate Session in other objects, like _userRepo and _catRepo you'll also need a way to access the Session there independent from the container, since it got nothing to do with how/when you instantiate the objects. Of course, you could pass it along like:

public void DoSomeWork()
{
    using(IUnitOfWorkSession session = _unitOfWork.Begin())
    {    
        _userRepo.Add(session, someUser);
        _catRepo.Add(session, someCat);
        session.Commit();
    }
}

But that's not really cool. So instead you will need to use something like a ThreadLocal (if you're using async/await this could be problematic) or otherwise a SynchronizationContext-local storage.

Now if you've managed to get this far you can look into doing AOP. AOP would have two handle two things:

  • getting access to the IUnitOfWork.
    • could be done by weaving it as additional ctor argument
    • or it could check if there is already one, if there is, it could use this one, if not, it could either throw or weave the argument in...
  • wrapping the decorated method like with the same code as above:

public void DoSomeWork()
{
    using(IUnitOfWorkSession session = _unitOfWork.Begin()) // weave this
    {    
        _userRepo.Add(session, someUser);
        _catRepo.Add(session, someCat);
        session.Commit(); // weave this
    }
}

这篇关于ninject注入iunitofwork到存储库作用域属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆