.NET CORE如何更改已注册服务的生命周期?

前言

我们知道在 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 依赖注入容器。