即使微软声称
replace the built-in container
这看起来似乎不那么简单,甚至不可能。
如所述
Steven
在他的第一条评论中,如果你选择使用你选择的容器,你应该并排运行它们。
微软的指导建议改变
ConfigureServices
在
Startup
从中分类:
public void ConfigureServices(IServiceCollection services)
{
// registrations into services
}
致:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var container = new YourContainer(); // Castle, Ninject, etc.
// registrations into container
return new YourContainerAdapter(container);
}
但是,这有很多问题,因为在
services
我们不一定知道如何在自己的容器中重新注册。嗯,有一个描述符,所以如果我们的容器支持所有不同的方法,那么实际上可以重新注册所有组件。在注册和服务解析方面,不同的DI容器有不同的机制。它们中的一些在这两者之间有着很难区分的地方,这使得有时很难适应“通用”的解决方案。
我最初的想法是提供一个接受
二者都
我自己的容器以及
服务
然后通过调用从中获取内置服务提供商的集合
services.BuildServiceProvider()
. 这样,我可以尝试从内置提供程序解析,然后,如果解析位失败,则尝试从我自己的容器解析。然而,事实证明.NET核心实现确实
不
使用返回的
IServiceProvder
实例。
我唯一能让它工作的方法是连接我自己的容器并使用它来解析我的控制器。这可以通过提供
IControllerActivator
接口。
在这个特定的实现中,我在处理ninject,尽管我通常更喜欢castle,但这同样适用于任何DI容器:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IKernel>(new StandardKernel());
services.AddSingleton<IControllerActivator, ControllerActivator>();
}
public class ControllerActivator : IControllerActivator
{
private readonly IKernel _kernel;
public ControllerActivator(IKernel kernel)
{
Guard.AgainstNull(kernel, nameof(kernel));
_kernel = kernel;
}
public object Create(ControllerContext context)
{
return _kernel.Get(context.ActionDescriptor.ControllerTypeInfo.AsType());
}
public void Release(ControllerContext context, object controller)
{
_kernel.Release(controller);
}
}
为了注册控制器类型,我在
Configure
方法,因为我可以访问
IApplicationBuilder
可用于获取控制器类型:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime applicationLifetime)
{
var kernel = app.ApplicationServices.GetService<IKernel>();
// kernel registrations
var applicationPartManager = app.ApplicationServices.GetRequiredService<ApplicationPartManager>();
var controllerFeature = new ControllerFeature();
applicationPartManager.PopulateFeature(controllerFeature);
foreach (var type in controllerFeature.Controllers.Select(t => t.AsType()))
{
kernel.Bind(type).ToSelf().InTransientScope();
}
applicationLifetime.ApplicationStopping.Register(OnShutdown);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors(
options => options.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()
);
app.UseMvc();
}
这对控制器很有效,但是解决“过滤器”仍然是一个问题,因为它们使用
IFilterFactory
在筛选器本身上实现工厂方法:
public IFilterMetadata CreateInstance (IServiceProvider serviceProvider);
在这里我们可以看到
IServiceProvider
提供实现以解决任何依赖。这适用于使用
TypeFilterAttribute
或者在定义继承自
类型筛选属性
就像我在问的那样。
这个机制实际上是“控制反转”和“依赖注入”之间区别的一个很好的例子。控制取决于框架(反转),我们必须提供相关的实现。唯一的问题是我们
不
自我们提供后,能够正确地钩住
ISeviceProvider(ISeviceProvider)
实例是
不
传递给
CreateInstance
方法,然后在尝试创建筛选器实例时导致失败。有很多方法可以修复这个设计,但我们会把它留给微软。
为了让我的过滤器正常工作,我决定按照史蒂文的暗示走“交叉布线”路线,只需在
服务
同时收集:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IKernel>(new StandardKernel());
services.AddSingleton<IControllerActivator, ControllerActivator>();
services.AddSingleton<IDatabaseContextFactory, DatabaseContextFactory>();
services.AddSingleton<IDatabaseGateway, DatabaseGateway>();
services.AddSingleton<IDatabaseContextCache, ContextDatabaseContextCache>();
// and so on
}
因为我的过滤器中没有太多的依赖项,所以它可以正常工作。这个
做
这意味着我们有“重复”的注册,我们需要根据实例的使用方式小心处理这些注册。
我想另一个选择可能是放弃DI容器的选择,只使用内置容器。