如何更改已注册服务的生命周期?【转】
前言
我们知道在 ASP.NET Core 中,内置了一个依赖注入容器,可用于注册和解析服务。
在注册服务时,我们需要指定服务的生命周期:
-
Transient:每次请求服务时都会创建一个新的实例。
-
Scoped:每次请求服务时都会创建一个新的实例,但在同一个请求内,每次请求服务时都会使用同一个实例。
-
Singleton:每次请求服务时都会使用同一个实例。
但在某些情况下,比如由第三方库内置的扩展方法依赖注入,已注册的服务的生命周期可能不是我们想要使用的。
直接修改库的源代码是不可取的,因为这样做会导致在更新库时丢失更改。
解决思路
ASP.NET Core 依赖注入容器的接口定义如下:
public interface IServiceCollection : ICollection<ServiceDescriptor>, IEnumerable<ServiceDescriptor>, IEnumerable, IList<ServiceDescriptor>
其中,ServiceDescriptor 是服务的描述符,它包含服务的类型、实现和生命周期:
public class ServiceDescriptor
{
public Type? ImplementationType { get; }
public object? ImplementationInstance { get; }
public Func<IServiceProvider, object>? ImplementationFactory { get; }
public ServiceLifetime Lifetime { get; }
public Type ServiceType { get; }
}
但是,这里的 Lifetime 属性是只读的,因此不能更改现有描述符的生命周期。
相反,我们可以获取并将其属性复制到新描述符中,并设置不同的新属性值,然后在依赖注入容器中删除旧描述符并添加新描述符。
实现
首先,我们需要一个扩展方法,它可以从集合中获取一个服务描述符:
public static class ServiceCollectionExtensions
{
public static ServiceDescriptor GetServiceDescriptor<TService>(this IServiceCollection services)
{
return services.FirstOrDefault(d => d.ServiceType == typeof(TService));
}
}
然后,我们需要一个扩展方法,它可以更改服务描述符的生命周期:
public static class ServiceCollectionExtensions
{
public static IServiceCollection ChangeServiceLifetime<TService>(this IServiceCollection services, ServiceLifetime lifetime)
{
var descriptor = services.GetServiceDescriptor<TService>();
if (descriptor != null)
{
services.Remove(descriptor);
if (descriptor.ImplementationFactory == null)
{
services.Add(new ServiceDescriptor(
descriptor.ServiceType,
descriptor.ImplementationType,
lifetime
));
}
else
{
services.Add(new ServiceDescriptor(
descriptor.ServiceType,
descriptor.ImplementationFactory,
lifetime
));
}
}
return services;
}
}
然后,我们就可以使用 ChangeServiceLifetime 方法更改服务的生命周期:
builder.Services.ChangeServiceLifetime<ISingletonService>(ServiceLifetime.Transient);
总结
通过本文,我们了解了如何更改已注册服务的生命周期。这是一个非常有用的技术,可以帮助我们更好地使用 ASP.NET Core 依赖注入容器。