NetCore提供了方法的DI框架,并在AspNetCore中大量使用了该注入框架。它是通过扩展包的形式提供出来,使用时通过Nuget包Microsoft.Extensions.DependencyInjection来使用。相对与其他DI框架,它比较轻量,且只支持构造器注入,不支持属性注入、以及方法注入。
使用起来比较简单,一般步骤如下所示:
// 1. 注册服务
var serviceCollection = new ServiceCollection();
.AddSingleton<IFoo, Foo>()
.AddTransient<IBar, Bar>()
.BuildServiceProvider())
// 2. 构建服务提供者
ServiceProvider serviceProvider serviceCollection.BuildServiceProvider();
// 3.然后其他地方都可以通过这个Provider.GetService()方法来获取服务
var IFoo = serviceProvider.GetService<IFoo>();
服务的注册方式
服务一般有以下几种方式:
- serviceCollection.Add(ServiceDescriptor item)
- serviceCollection.AddXXX<TService, TImplementation>()
- serviceCollection.AddXXX(Func<IServiceProvider, TService> implementationFactory)
下面几句作用相同
serviceCollection.AddTransient<IFoo,Foo>();
serviceCollection.Add(new ServiceDescriptor(typeof(IFoo),typeof(Foo), ServiceLifetime.Transient));
serviceCollection.AddTransient<IFoo>(provider => new Foo());
服务提供者 ServiceProvider
服务实例的创建、销毁均由ServiceProvider负责维护,生成的服务实例一般不要执行对象的Dispose方法(如果有的话),因为服务实例有可能被其他地方使用。
当释放ServiceProvider时,该Provider将会释放由它生成的对象。
在生成实例时,如果对象实现了Dispose接口,它会把该对象保存在_disposables列表中,当Provider释放时,当即会自动调用_disposables元素对象的Dispose方法
而ServiceProvider本身又可以创建新的ServiceProvider,这样就相当生成了一个作用域 Scope。
服务的生命周期
生命周期分为三种:
- 瞬态 Transient: 每一次从ServiceProvider取得,均返回一个新的实例。
- 作用域 Scope:在同一作用域下生成的实例相同,即同一个ServiceProvider下取得的实例相同
- 单例 Singleton: 任意一个ServiceProvider下取得的实例都相同
作用域主要针对特定ServiceProvider的,即在同一个ServiceProvider下,注册为Scope类型的对象为单例
示例
using (ServiceProvider rootProvider = new ServiceCollection()
.AddTransient<IFoo, Foo>()
.AddScope<IBar, Bar>()
.AddSingleton<IFoobar, Foobar>()
.BuildServiceProvider())
{
IFoo foo = rootProvider.GetService<IFoo>();
IFoo foo2 = rootProvider.GetService<IFoo>();
IBar bar = rootProvider.GetService<IBar>();
IBar bar2 = rootProvider.GetService<IBar>();
IFoobar foobar = rootProvider.GetService<IFoobar>();
IFoobar foobar2 = rootProvider.GetService<IFoobar>();
IFoo foo3 = null;
IBar bar3 = null;
IFoobar foobar3 = null;
using(var scope = rootProvider.CreateScope())
{
var childProvider = scope.ServiceProvider;
foo3 = childProvider.GetService<IFoo>();
bar3 = childProvider.GetService<IBar>();
foobar3 = childProvider.GetService<IFoobar>();
}
var childProvider2 = rootProvider.CreateScope().ServiceProvider;
IFoo foo3 = childProvider2.GetService<IFoo>();
// 执行点1
}
// 执行点2
实例对象说明
- IFoo 注册为Transient,则 foo foo2,foo3 均不等
- IBar 注册为Scope,则 bar==bar2(因为他们都是同一个rootProvider提供),bar2 <> bar2(因为他们是不同的Provider提供,一个为rootProvider,一个为childProvider)
- IFoobar 注册为Singleton,所以 foobar == foobar2 == foobar3
释放实例时机
- 执行到【执行点1】时,释放:
- childProvider本身,childProvider再释放由其创建的 bar3、foo3
- bar3
- foo3
- 执行到【执行点2】时,释放:
- rootProvider本身
- foo
- foo2
- bar, (没有bar2,因为bar2就是bar)
- childProvider2,以及由childProvider2创建的foo3
在ASP.Net Core中使用
在asp.net core中主要有两个ServiceProvider,可以通过它们手动取到对应的服务实例
- WebHost中的ServiceProvider,即 webHost.Services
- Http请求中HttpContext的ServiceProvider,即 HttpContext.RequestServices
它们的关系是父子关系:Http请求中ServiceProvider是中间件RequestServicesContainerMiddleware
根据WebHost的ServiceProvider生成。
Http请求结束后,将会释放Http请求中ServiceProvider,并释放掉该作用域下的实例对象
注入方式
public class SampleController ControllerBase
{
// 1.第一种:构造函数注入
private IService _service = null;
public SampleController(IService service)
{
this._service = service;
}
// 2.第二种:通过方法注入
[HttpPost]
public IActionResult Method1([FromServices] IService service) => service.DoSomething()
// 3.第三种:通过HttpContext,手动取得
[HttpGet]
public IActionResult Method2()
{
IService service = HttpContext.RequestServices.GetService<>(IService);
...
}
...
}
使用其他第三成熟的Ioc框架(不推荐使用)
官方DI框架它可以被其他成熟的Ioc框架所代替,但是可以把.NetCore DI框架作为一个“包装器”,可以插入其他DI框架(例如 Autofac、Unity等)
插入其他DI框架的目的,主要是想利用其它框架一些优点,比如批量注册、属性注入等。
替换的方法,主要是再注册服务方法ConfigureServices的时候,用第三方DI扩展实现的IServiceProvider替换掉官方DI的IServiceProvider。
目前实现了官方DI扩展的第三方框架有 AspectCore、Autofac、Unity,虽然使用第三方DI框架带来了一些便利,但同时带来了对于第三方DI容器特性的依赖
在Asp.net Core中使用 Autofac
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
return RegisterAutofac(services);
}
private IServiceProvider RegisterAutofac(IServiceCollection services)
{
var builder = new ContainerBuilder();
builder.Populate(services);
var assembly = this.GetType().GetTypeInfo().Assembly;
builder.RegisterType<AopInterceptor>();
builder.RegisterAssemblyTypes(assembly)
.Where(type =>
typeof(IDependency).IsAssignableFrom(type) && !type.GetTypeInfo().IsAbstract)
.AsImplementedInterfaces()
.InstancePerLifetimeScope().EnableInterfaceInterceptors().InterceptedBy(typeof(AopInterceptor));
this.ApplicationContainer = builder.Build();
return new AutofacServiceProvider(this.ApplicationContainer);
}