寻找行为类似InRequestScope的Ninject范围

Rus*_*ino 6 ninject unit-of-work repository-pattern entity-framework-5

在我的服务层上,我UnitOfWork在构造函数中注入了一个和两个存储库.工作单元和存储库有一个DbContext我想在两者之间共享的实例.我怎么能用Ninject做到这一点?应考虑哪个范围?

不在网络应用程序中,所以我无法使用InRequestScope.

我尝试做类似的事情......然而我正在使用DI,我需要我的UoW Dispose并且像这样创建.

using (IUnitOfWork uow = new UnitOfWorkFactory.Create())
{
    _testARepository.Insert(a);
    _testBRepository.Insert(b);

    uow.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)

编辑:我只是想确定我理解...看看https://github.com/ninject/ninject.extensions.namedscope/wiki/InNamedScope我虽然关于我当前使用Ninject的控制台应用程序架构.

让我们说:

A类是服务层类

B类是一个工作单元,它接受参数接口(IContextFactory)

C类是一个接口参数的存储库(IContextFactory)

这里的想法是能够在2个或更多存储库上执行上下文操作,并使用工作单元来应用更改.

D类是一个上下文工厂(实体框架),它提供了一个实例(保存在容器中)的上下文,它在Class B et C之间共享(并且将用于其他存储库).

上下文工厂将实例保存在他的容器中,所以我不想重复使用这个实例的所有名称,因为上下文需要在服务操作结束时处理..实际上InNamedScope的主要目的是什么?

解决方案是,但我不确定我做得对,服务实例将是transcient,这意味着他们实际上从未处理过?:

Bind<IScsContextFactory>()
    .To<ScsContextFactory>()
    .InNamedScope("ServiceScope")
    .WithConstructorArgument(
         "connectionString", 
         ConfigurationUtility.GetConnectionString());

Bind<IUnitOfWork>().To<ScsUnitOfWork>();

Bind<IAccountRepository>().To<AccountRepository>();
Bind<IBlockedIpRepository>().To<BlockedIpRepository>();

Bind<IAccountService>().To<AccountService>().DefinesNamedScope("ServiceScope");
Bind<IBlockedIpService>().To<BlockedIpService>().DefinesNamedScope("ServiceScope");
Run Code Online (Sandbox Code Playgroud)

Rub*_*ink 5

更新:这种方法对NuGet当前工作有效,但是在InCallscope实现中存在异常,这在当前的Unstable NuGet包中得到了修复.我会在几天内调整这个答案,以反映一些仔细研究后的最佳方法.注意,构建东西的高级方式将保持完全相同,只需Bind<DbContext>()确定范围的确切细节即可.(提示:CreateNamedScope在不稳定的情况下可以工作,或者可以将命令处理程序设置为DefinesNamedScope.原因我不这样做是因为我希望有一些组合/播放的东西 InRequestScope)


我强烈建议您阅读Ninject.Extensions.NamedScope集成测试(严肃地说,找到它们并阅读并重新阅读它们)

DbContext 工作单位所以不需要进一步包装.

由于您希望能够在飞行中拥有多个"请求"并希望在它们之间共享一个工作单元,您需要:

Bind<DbContext>()
    .ToMethod( ctx => 
        new DbContext( 
            connectionStringName: ConfigurationUtility.GetConnectionString() ))
    .InCallScope();
Run Code Online (Sandbox Code Playgroud)

InCallScope()意思是说:

  1. 对于为单个kernel.Get() 调用组成的给定对象图(因此在调用范围内),需要一个的每个人DbContext将获得相同的实例.
  2. 这个IDisposable.Dispose()Kernel.Release()在根对象发生时调用(或者Kernel.Components.Get<ICache>().Clear()如果不是则发生根则调用.InCallScope())

应该没有理由使用InNamedScope()DefinesNamedScope(); 您没有尝试从默认池/父级/分组中排除的长期对象.

如果您执行上述操作,您应该能够:

var command = kernel.Get<ICommand>();
try {
    command.Execute();
} finally {
    kernel.Components.Get<ICache>().Clear( command ); // Dispose of DbContext happens here
}
Run Code Online (Sandbox Code Playgroud)

Command实现如下:

class Command : ICommand {
    readonly IAccountRepository _ar;
    readonly IBlockedIpRepository _br;
    readonly DbContext _ctx;
    public Command(IAccountRepository ar, IBlockedIpRepository br, DbContext ctx){
        _ar = ar;
        _br = br;
        _ctx = ctx;
    }
    void ICommand.Execute(){
        _ar.Insert(a);
        _br.Insert(b);
        _ctx.saveChanges();
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,一般来说,我以这种方式避免使用隐含的工作单元,而是表现它的创建和Disposal.这使得Command看起来像这样:

class Command : ICommand {
    readonly IAccountService _as;
    readonly IBlockedIpService _bs;
    readonly Func<DbContext> _createContext;
    public Command(IAccountService @as, IBlockedIpServices bs, Func<DbContext> createContext){
        _as = @as;
        _bs = bs;
        _createContext = createContext;
    }
    void ICommand.Execute(){
        using(var ctx = _createContext()) {
            _ar.InsertA(ctx);
            _br.InsertB(ctx);
            ctx.saveChanges();
        }
   }
Run Code Online (Sandbox Code Playgroud)

这不涉及的使用.InCallScope()Bind<DbContext>()(但要求存在Ninject.Extensions.FactoryFactoryModule合成Func<DbContext>,从一个简单的Bind<DbContext>().