C#笔记2 依赖注入—服务的生命周期
服务的生命周期
示例用到的包和命名空间
包
Microsoft.Extensions.DependencyInjection
命名空间
Microsoft.Extensions.DependencyInjection
使用单例和临时服务
RegisterServices在方法SingletonAndTransient中作为本地函数实现。其中注册了ServiceA,ServiceB,NumberService和控制器类ControllerX。NumberService和ServiceA注册为单例,ServiceB和ControllerX注册为瞬态。
public static void SingletonAndTransient()
{
Console.WriteLine(nameof(SingletonOrTransient));
static ServiceProvider RegisterService()
{
IServiceCollection services = new ServiceCollection();
services.AddSingleton<IServiceA, ServiceA>();
services.AddTransient<IServiceB, ServiceB>();
services.AddTransient<ControllerX>();
services.AddSingleton<INumberService, NumberService>();
return services.BuildServiceProvider();
}
//TODO
}
AddSingleton和AddTransient都是扩展方法,更便于用Microsoft.Extensions.DependencyInjection框架注册服务。除了这些有用的方法之外,还可以使用Add方法注册。Add方法需要一个包含服务类型,实现类型和服务种类的ServiceDescriptor加粗样式。服务的种类使用ServiceLifetime枚举类型指定。ServiceLifetime定义了值Singleton,Transient和Scoped。
注意
ServiceCollection类的Add方法是为接口IServiceCollection显示实现的。对于这个,只能在使用接口IServiceCollection时才能看到方法,使用ServiceCollection类型的对象时是看不到他的。
使用Scope服务
服务也可以在一个作用域内注册,这是介于Singleton和Transient之间的服务。Singleton之创建一个实例,Transient每次请求服务都会创建一个新实例。对于Scope,总是从相同的作用域中返回相同的实例,但是从不同的作用域中会返回不同的实例。作用域默认用ASP.NET Core Web应用程序定义。这里,作用域是个HTTP Web请求。对于Scope服务,如果对容器的请求来自同一个HTTP请求,则返回相同的实例;如果来自不同的HTTP请求,则返回不同是实例。这允许在HTTP请求中轻松的 共享状态。
对于非ASP.NET Core Web应用程序,需要自己创建作用域,以获得Scope服务的优势。
private static void UsingScope()
{
ServiceProvider RegisterServices()
{
var services = new ServiceCollection();
services.AddSingleton<INumberService, NumberService>();
services.AddScoped<IServiceA,ServiceA>();
services.AddSingleton<IServiceB, ServiceB>();
services.AddTransient<IServiceC, ServiceC>();
}
//TODO
}
调用ServiceProvider的CreateScope方法可以创建一个作用域。这将返回实现接口IServiceScope的作用域对象,在其中可以访问属于这个作用域的ServiceProvider,可以在容器中请求服务。
private static void UsingScope()
{
//TODO
using(ServiceProvider container = RegisterServices())
{
using(IServiceScope scope = container.CreateScope())
{
IserviceA a1 = scope.ServiceProvider.GetService<IServiceA>();
//TODO
}
}
}
注意:
不需要在服务上调用Dispose方法来释放它们,使用实现了IDispose接口的服务,容器会调用Dispose方法。当释放作用域时,将释放Transient服务和Scoped服务。在释放根提供程序时,将释放Singleton服务。
在.Net Core 2.0中,服务实例按照创建的相反顺序来释放,当一个服务需要注入另一个服务时这很重要。
使用自定义工厂或传递现有实例
除了定义使用transient、scoped和singleton之外,还可以创建自定义工厂或者将现有实例传递给容器。
- 可以使用AddSingleton方法的重载版本,将先前创建的实例传递给容器。这里,在RegisterServices方法中,首先创建一个NumberService对象,然后将其传递给AddSingleton方法。使用GetService方法,或者在构造函数中注入它,与前面的的代码没什么不同。只需要注意,在本例中,容器不负责调用Dispose方法。对于创建并传递到容器中的服务,应由开发人员手动释放(如果需要释放)。
- 还可以使用工厂方法来创建实例,而不是从容器中创建服务。如果服务需要自定义的初始化,或者定义不受DI容器支持的构造函数,这是一个有用的选项。可以通过IServiceProvider参数传递委托,并将服务实例返回到AddSingleton、AddScoped和AddTransient方法。
public static void CustomFactories()
{
Console.WriteLine(nameof(CustomFactories));
IServiceB CreateServiceFactory(IServiceProvider serviceProvider)
=> new ServiceB(serviceProvider.GetService<INumberService>());
ServiceProvider RegisterService()
{
var numberService = new NumberService();
IServiceCollection services = new ServiceCollection();
services.AddSingleton(numberService);// use a existing
services.AddTransient(CreateServiceFactory);// use a custom factory
return services.BuildServiceProvider();
}
}
推荐阅读