寻找行为类似InRequestScope的Ninject范围

时间:2013-03-24 13:56:41

标签: ninject entity-framework-5 repository-pattern unit-of-work

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

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

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

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

    uow.SaveChanges();
}

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

让我们说:

A类是服务层类

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

C类是一个存储库,它接受参数接口(IContextFactory)

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

D类是一个上下文工厂(实体框架),它提供了一个实例(保存在容器中)的上下文,它在B类和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");

2 个答案:

答案 0 :(得分:5)

更新:这种方法对NuGet当前有效,但是在InCallscope实现中存在一个异常现象,该异常已在当前的Unstable NuGet包中修复。我会在几天内调整这个答案,以反映一些仔细研究后的最佳方法。注意,构建内容的高级方式将保持完全相同,只需Bind<DbContext>()范围确切的详细信息即可。 (提示:不稳定中的CreateNamedScope会起作用,或者可以将命令处理程序设置为DefinesNamedScope。原因我不这样做是因为我希望能够创建一些与{{1}组合/播放良好的内容})


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

Ninject.Extensions.NamedScope 是工作单位,因此无需进一步换行。

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

DbContext

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

  1. 针对单个InCallScope() 调用(因此在调用范围内)组成的给定对象图,需要kernel.Get()的所有人都将获得相同的实例。
  2. DbContextIDisposable将在根对象发生Dispose()时调用(如果根Kernel.Release()不是Kernel.Components.Get<ICache>().Clear(),则会调用.InCallScope() InNamedScope() })
  3. 没有理由使用DefinesNamedScope()var command = kernel.Get<ICommand>(); try { command.Execute(); } finally { kernel.Components.Get<ICache>().Clear( command ); // Dispose of DbContext happens here } ;您没有尝试从默认池/父母/分组中排除的长期对象。

    如果你这样做,你应该能够:

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

    Command实现如下:

    Disposal

    请注意,一般来说,我避免以这种方式拥有隐含的工作单元,而是表现它的创建和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(); } } 。这使命令看起来像这样:

    .InCallScope()

    这不会在Bind<DbContext>()上使用Func<DbContext>(但需要Ninject.Extensions.Factory's FactoryModule的存在才能从简单的Bind<DbContext>()合成{{1}}。< / p>

答案 1 :(得分:2)

the other answer中所述,InCallScope不是解决此问题的好方法。

目前我正在转发一些代码,这些代码违反最新的NuGet Unstable / Include PreRelease / Instal-Package -Pre版本的Ninject.Web.Common,但没有明确的解释。我会将此翻译为Ninject.Extensions.NamedScope wiki中某篇文章已开始在the Ninject.Extensions.NamedScope wiki's CreateNamedScope/GetScope article中编写此技术的演练。

可能某些位也会在某个阶段成为Pull Request(给我提供大纲代码的@Remo Gloor)。 associated tests and learning tests are in this gist for now),待处理的包装以正确发布的格式TBD。

exec摘要是您将下面的模块加载到内核中,并在每个处理程序调用上使用.InRequestScope() / Dispose每个处理程序调用,然后通过IHandlerComposer.ComposeCallDispose提供请求。< / p>

如果您使用以下模块:

public class Module : NinjectModule
{
    public override void Load()
    {
        Bind<IHandlerComposer>().To<NinjectRequestScopedHandlerComposer>();

        // Wire it up so InRequestScope will work for Handler scopes
        Bind<INinjectRequestHandlerScopeFactory>().To<NinjectRequestHandlerScopeFactory>();
        NinjectRequestHandlerScopeFactory.NinjectHttpApplicationPlugin.RegisterIn( Kernel );
    }
}

工厂[1]和NinjectHttpApplicationPlugin中哪些线路暴露:

public interface INinjectRequestHandlerScopeFactory
{
    NamedScope CreateRequestHandlerScope();
}

然后您可以使用此Composer运行请求InRequestScope()

public interface IHandlerComposer
{
    void ComposeCallDispose( Type type, Action<object> callback );
}

实施为:

class NinjectRequestScopedHandlerComposer : IHandlerComposer
{
    readonly INinjectRequestHandlerScopeFactory _requestHandlerScopeFactory;

    public NinjectRequestScopedHandlerComposer( INinjectRequestHandlerScopeFactory requestHandlerScopeFactory )
    {
        _requestHandlerScopeFactory = requestHandlerScopeFactory;
    }

    void IHandlerComposer.ComposeCallDispose( Type handlerType, Action<object> callback )
    {
        using ( var resolutionRoot = _requestHandlerScopeFactory.CreateRequestHandlerScope() )
            foreach ( object handler in resolutionRoot.GetAll( handlerType ) )
                callback( handler );
    }
}

Ninject Infrastructure的东西:

class NinjectRequestHandlerScopeFactory : INinjectRequestHandlerScopeFactory
{
    internal const string ScopeName = "Handler";

    readonly IKernel _kernel;

    public NinjectRequestHandlerScopeFactory( IKernel kernel )
    {
        _kernel = kernel;
    }

    NamedScope INinjectRequestHandlerScopeFactory.CreateRequestHandlerScope()
    {
        return _kernel.CreateNamedScope( ScopeName );
    }

    /// <summary>
    /// When plugged in as a Ninject Kernel Component via <c>RegisterIn(IKernel)</c>, makes the Named Scope generated during IHandlerFactory.RunAndDispose available for use via the Ninject.Web.Common's <c>.InRequestScope()</c> Binding extension.
    /// </summary>
    public class NinjectHttpApplicationPlugin : NinjectComponent, INinjectHttpApplicationPlugin
    {
        readonly IKernel kernel;

        public static void RegisterIn( IKernel kernel )
        {
            kernel.Components.Add<INinjectHttpApplicationPlugin, NinjectHttpApplicationPlugin>();
        }

        public NinjectHttpApplicationPlugin( IKernel kernel )
        {
            this.kernel = kernel;
        }

        object INinjectHttpApplicationPlugin.GetRequestScope( IContext context )
        {
            // TODO PR for TrgGetScope
            try
            {
                return NamedScopeExtensionMethods.GetScope( context, ScopeName );
            }
            catch ( UnknownScopeException )
            {
                return null;
            }
        }

        void INinjectHttpApplicationPlugin.Start()
        {
        }

        void INinjectHttpApplicationPlugin.Stop()
        {
        }
    }
}