寻找行为类似于InRequestScope的Ninject范围 [英] Looking for a Ninject scope that behaves like InRequestScope

查看:75
本文介绍了寻找行为类似于InRequestScope的Ninject范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

On my service layer I have injected an UnitOfWork and 2 repositories in the constructor. The Unit of Work and repository have an instance of a DbContext I want to share between the two of them. How can I do that with Ninject ? Which scope should be considered ?

不在Web应用程序中,所以我不能使用InRequestScope.

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

I try to do something similar... and I am using DI however, I need my UoW to be Disposed and created like this.

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

    uow.SaveChanges();
}

我只想确保我理解...看完 https: //github.com/ninject/ninject.extensions.namedscope/wiki/InNamedScope 关于我当前实际使用Ninject的控制台应用程序体系结构的信息.

I just want to be sure i understand… after look at https://github.com/ninject/ninject.extensions.namedscope/wiki/InNamedScope i though about my current console application architecture which actually use Ninject.

让我们说:

A类是服务层类

B类是将接口(IContextFactory)作为参数的工作单元

Class B is an unit of work which take into parameter an interface (IContextFactory)

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

Class C is a repository which take into parameter an interface (IContextFactory)

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

The idea here is to be able to do context operations on 2 or more repository and using the unit of work to apply the changes.

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

Class D is a context factory (Entity Framework) which provide an instance (keep in a container) of the context which is shared between Class B et C (.. and would be for other repositories aswell).

上下文工厂将实例保存在他的容器中,所以我不想重用该实例的所有名称,因为上下文需要在服务操作的结尾处放置..这实际上是InNamedScope的主要目的?

The context factory keep the instance in his container so i don’t want to reuse this instance all the name since the context need to be disposed at the end of the service operaiton.. it is the main purpose of the InNamedScope actually ?

解决方案是,但是我不确定我是否做对了,服务实例将是过时的,这意味着它们实际上从未处置过? :

The solution would be but i am not sure at all i am doing it right, the services instance gonna be transcient which mean they actually never disposed ? :

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");

推荐答案

更新:此方法适用于当前的NuGet,但依赖于InCallscope实现中的异常,该异常已在当前的不稳定NuGet程序包中修复.经过几天的思考,我将在几天后调整这个答案,以反映最佳方法.注意,结构化内容的高级方式将保持几乎相同,只是Bind<DbContext>()作用域的确切细节将起作用. (提示:CreateNamedScope在不稳定状态下可以工作,或者可以将命令处理程序设置为DefinesNamedScope.我之所以这样做并不是因为我想拥有一些可以与InRequestScope完美搭配/播放的东西)

UPDATE: This approach works against NuGet current, but relies in an anomaly in the InCallscope implementation which has been fixed in the current Unstable NuGet packages. I'll be tweaking this answer in a few days to reflect the best approach after some mulling over. NB the high level way of structuring stuff will stay pretty much identical, just the exact details of the Bind<DbContext>() scoping will work. (Hint: CreateNamedScope in unstable would work or one could set up the Command Handler as DefinesNamedScope. Reason I dont just do that is that I want to have something that composes/plays well with InRequestScope)

我强烈建议您阅读Ninject.Extensions.NamedScope集成测试(认真地找到它们,然后阅读并重新阅读它们)

I highly recommend reading the Ninject.Extensions.NamedScope integration tests (seriously, find them and read and re-read them)

DbContext 是一个工作单元,因此无需进一步包装.

The DbContext is a Unit Of Work so no further wrapping is necessary.

由于您希望能够同时执行多个请求",并且希望在它们之间共享单个工作单元,因此您需要:

As you want to be able to have multiple 'requests' in flight and want to have a single Unit of Work shared between them, you need to:

Bind<DbContext>()
    .ToMethod( ctx => 
        new DbContext( 
            connectionStringName: ConfigurationUtility.GetConnectionString() ))
    .InCallScope();

InCallScope()表示:

  1. 对于由单个kernel.Get() Call 组成的给定对象图(因此在 Call 范围内),每个需要DbContext的对象都将获得相同的实例.
  2. IDisposable.Dispose()将在根对象出现Kernel.Release()时被调用(或者,如果不是.InCallScope()则在根出现Kernel.Components.Get<ICache>().Clear())将被调用
  1. for a given object graph composed for a single kernel.Get() Call (hence In Call Scope), everyone that requires an DbContext will get the same instance.
  2. the IDisposable.Dispose() will be called when a Kernel.Release() happens for the root object (or a Kernel.Components.Get<ICache>().Clear() happens for the root if it is not .InCallScope())

应该没有理由使用InNamedScope()DefinesNamedScope();您没有要从默认池/育儿/分组中排除的寿命长的对象.

There should be no reason to use InNamedScope() and DefinesNamedScope(); You don't have long-lived objects you're trying to exclude from the default pooling / parenting / grouping.

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

If you do the above, you should be able to:

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

Command实现如下:

The Command implementation looks like:

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


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


Note that in general, I avoid having an implicit Unit of Work in this way, and instead surface it's creation and Disposal. This makes a Command look like this:

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

这不涉及在Bind<DbContext>()上使用.InCallScope()(但确实需要存在

This involves no usage of .InCallScope() on the Bind<DbContext>() (but does require the presence of Ninject.Extensions.Factory's FactoryModule to synthesize the Func<DbContext> from a straightforward Bind<DbContext>().

这篇关于寻找行为类似于InRequestScope的Ninject范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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