6

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

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

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

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

2 回答 2

5

更新:此方法适用于当前的 NuGet,但依赖于InCallscope实现中的异常,该异常已在当前的 Unstable NuGet 包中修复。我将在几天内调整这个答案,以反映经过一番思考后的最佳方法。请注意,构建东西的高级方式将保持几乎相同,只是Bind<DbContext>()范围界定的确切细节将起作用。(提示:CreateNamedScope在不稳定的情况下可以工作,或者可以将命令处理程序设置为DefinesNamedScope。我不这样做的原因是我想要一些可以很好地组合/播放的东西 InRequestScope


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

DbContext 一个工作单元,因此不需要进一步包装。

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

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

InCallScope()意味着:

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

应该没有理由使用InNamedScope()and 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();
    }
}

请注意,一般情况下,我会避免以这种方式使用隐式工作单元,而是将它的创建和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()on the 的使用Bind<DbContext>()(但确实需要Ninject.Extensions.Factory'sFactoryModule的存在才能Func<DbContext>从简单的Bind<DbContext>().

于 2013-03-24T22:22:35.840 回答
2

正如在另一个答案中所讨论的,InCallScope这不是解决这个问题的好方法。

现在,我正在倾销一些适用于最新 NuGet Unstable / Include PreRelease / Instal-Package -Pre版本的代码,Ninject.Web.Common但没有明确的解释。我将把它翻译成 wiki 中的一篇文章,Ninject.Extensions.NamedScope在某个阶段已经开始在 wiki 的 CreateNamedScope/GetScope 文章中编写此技术Ninject.Extensions.NamedScope演练

可能一些位也会在某个阶段成为拉取请求(向@Remo Gloor 致敬,他为我提供了大纲代码)。相关的测试和学习测试现在都在这个要点中),等待以适当的发布格式打包(待定)。

执行摘要是您将下面的模块加载到您的内核中,并在每个处理程序调用中使用.InRequestScope()您想要创建/ Disposed 的所有内容,然后通过IHandlerComposer.ComposeCallDispose.

如果您使用以下模块:

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 基础设施的东西:

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()
        {
        }
    }
}
于 2013-04-05T14:22:45.010 回答