寻找一个像InRequestScope一样的Ninject范围
在我的服务层上,我在构造函数中注入了一个UnitOfWork
和2个存储库。 工作单元和存储库有一个DbContext
实例,我想在它们之间共享。 我怎样才能和Ninject做到这一点? 应考虑哪个范围?
我不在一个Web应用程序,所以我不能使用InRequestScope
。
我尝试做类似的事情……而我正在使用DI,但是,我需要我的UoW来Dispose
d并创build它。
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类是一个上下文工厂(Entity Framework),它提供了一个在B类和C类之间共享的上下文的实例(保存在容器中),也可以用于其他的存储库。
上下文工厂保存在他的容器中的实例,所以我不想重复使用这个实例所有的名字,因为上下文需要被放置在服务operaiton的末尾..这实际上是InNamedScope的主要目的?
解决的办法是,但我不确定我是否做得对,服务实例会变成这样,这意味着他们实际上从来没有处置? :
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
实现中的一个exception,已经在当前的Unstable NuGet包中修复。 我会在几天内调整这个答案,以反映最好的方法,经过一番思考。 注意构造东西的高级方法将保持几乎相同,只是Bind<DbContext>()
范围的确切细节将起作用。 (提示:在不稳定的CreateNamedScope
将工作,或者可以设置命令处理程序作为DefinesNamedScope
原因我不只是这样做,我想有一些组成/使用InRequestScope
)
我强烈build议阅读Ninject.Extensions.NamedScope
集成testing(认真地find它们并阅读并重新读取它们)
DbContext
是工作单元,所以不需要进一步的包装。
由于您希望能够在飞行中拥有多个“请求”,并希望在他们之间共享单个工作单元,因此您需要:
Bind<DbContext>() .ToMethod( ctx => new DbContext( connectionStringName: ConfigurationUtility.GetConnectionString() )) .InCallScope();
InCallScope()
意味着:
- 对于为单个
kernel.Get()
调用 (因此在调用范围内)组成的给定对象图,每个需要DbContext
都将获得相同的实例。 -
IDisposable
。 当一个Kernel.Release()
发生在根对象(或一个Kernel.Components.Get<ICache>().Clear()
时Dispose()
将会被调用,如果不是.InCallScope()
)
应该没有理由使用InNamedScope()
和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(); } }
请注意,一般来说,我避免以这种方式有一个隐含的工作单元,而是表示它的创build和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(); } }
这不涉及在Bind<DbContext>()
(但是需要存在Ninject.Extensions.Factory
来从简单的Bind<DbContext>()
综合Func<DbContext>
。
正如在另一个答案中所讨论的 , InCallScope
不是解决这个问题的好方法。
现在我正在倾销一些代码,对Ninject.Web.Common的最新NuGet Unstable / Include PreRelease / Instal-Package -Pre
Ninject.Web.Common
版本没有明确的解释。 我将把它翻译成已经开始在Ninject.Extensions.NamedScope
wiki中的一篇文章,在某个阶段Ninject.Extensions.NamedScope
wiki的CreateNamedScope / GetScope文章中编写这种技术的演练。
可能有些位也会在某个阶段变成拉取请求(提示给@Remo Gloor谁给了我大纲代码)。 相关的testing和学习testing目前正处于这个要点中 ),等待包装成适当的发布格式TBD。
执行摘要是你加载下面的模块到您的内核和使用.InRequestScope()
在你想创build的一切/ Dispose
d处理程序调用,然后通过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 ); } }
Factory [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() { } } }