2014-10-30 86 views
2

让我从我当前的设置开始,然后解释我想实现的目标。我们使用NHibernate并试图用Ninject实现IRepository/IUnitOfWork模式。理想情况下,它应该适用于任何使用代码的应用程序,无论是ASP.Net,WCF还是其他。ninject注入iunitofwork到存储库作用域属性

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构造函数,它将使用与IRepo相同的UnitOfWork函数,无论它实际上可能有多少层。

但是我想要做的就是将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); 
} 

是否有任何形式的结合设置我能做到这将使我来标记与[交易]的方法也是这样吗?我愿意对IUnitOfWork和IRepository进行一些小的重构,而服务层代码只是废料代码,所以我可以在那里非常灵活。

回答

0

首先,注射不会

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

工作,容器不知道你会什么样的方法来调用,当你要调用它。

更进一步,因为您希望它与WebApps,WCF一起工作..可能是一些石英工作或不是:您必须决定是否要将使用它的对象的使用期限(如MyService)与您想要的服务生存期相关联(如IUnitOfWork/Repository/NHibernate Session)较长(例如单),并创建一个新的NHibernate Session对于像一个方法调用:

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

这事实上可能被AOP使用FodyPostSharp实现,无论是。或者,您也可以使用装饰器模式来实现此目的(请参阅here)。但是,如果我没有记错,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的Session中的其他对象,像_userRepo_catRepo您还需要一种方法来访问因为它与你如何/何时实例化对象无关。 当然,你可以沿着它传递这样的:

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

但是,这不是真的很酷。因此,您需要使用类似于ThreadLocal(如果您使用异步/等待,这可能会有问题)或其他SynchronizationContext本地存储。

现在,如果你已经设法达到这个目标,你可以考虑做AOP。 AOP将有两个处理两件事:

  • 获得访问IUnitOfWork。
    • 可以通过编织它作为额外的构造函数的参数来完成
    • 或者它可以检查是否已经有一个,如果有,它可以使用这一个,如果不是,就既可以抛出或编织论点...
  • 包裹装饰的方法,像上述相同代码:
public void DoSomeWork() 
{ 
    using(IUnitOfWorkSession session = _unitOfWork.Begin()) // weave this 
    {  
     _userRepo.Add(session, someUser); 
     _catRepo.Add(session, someCat); 
     session.Commit(); // weave this 
    } 
} 
+0

我们目前使用'PostSharp'(V2.1可悲)在其他组件,所以我知道我会用它来实现AOP。我的想法是,我的方面的OnEntry会开始一个新的'UnitOfWork'(我会忽略尝试附加到现有的),但我如何确保创建的'UnitOfWork'是注入每个' IRepository'?我是否需要通过将方面的'UnitOfWork'粘贴到某种上下文机制(ThreadLocal/SynchronizationContext,就像您所提到的)? – cjablonski76 2014-10-31 13:13:14

+0

既然(至少这就是我干涉的内容),你不能也不想将“Session”的生命周期与使用它的对象的生命周期联系起来,你确实需要使用像ThreadLocal这样的上下文机制或SynchronizationContext。 – BatteryBackupUnit 2014-10-31 13:44:33

+0

如果你不想使用这样的上下文机制,每次执行DoSomeWork时都需要创建一个新的用户回购,cat回购等。你可能最终会重新创建你的对象图中相当大的一部分(取决于设计...)。有了上下文机制,你只能创建'Session',但你仍然需要这样做(通过'IUnitOfWork.Begin()'或创建一个新的'IUnitOfWork'实例)。 – BatteryBackupUnit 2014-10-31 13:47:12