替换ASP.NET Core内置DI容器中的服务注册?

让我们考虑一下Startup.ConfigureServices的服务注册:

 public void ConfigureServices(IServiceCollection services) { services.AddTransient(); } 

调用FooB后,是否可以将IFoo注册更改为FooB ? 它有助于测试目的(例如,在TestStartup子类中)或者我们对代码库的访问是有限的。

如果我们注册另一个IFoo实现:

 services.AddTransient(); services.AddTransient(); 

然后GetService返回FooB而不是FooA

 IFoo service = services.BuildServiceProvider().GetService(); Assert.True(service is FooB); 

但是, GetServices成功返回两个实现(对于GetService<IEnumerable> ):

 var list = services.BuildServiceProvider().GetServices().ToList(); Assert.Equal(2, list.Count); 

IServiceCollection合同中有Remove(ServiceDescriptor)方法。 我应该如何使用ServiceDescriptor来修改服务注册?

使用ServiceCollectionDescriptorExtensions类中的Replace(IServiceCollection, ServiceDescriptor)方法很简单。

 // IFoo -> FooA services.AddTransient(); // Replace // IFoo -> FooB var descriptor = new ServiceDescriptor( typeof(IFoo), typeof(FooB), ServiceLifetime.Transient); services.Replace(descriptor); 

也可以看看:

  • ServiceDescriptor构造函数

如果你知道两件简单的事情,很容易覆盖ASP.NET Core DIfunction:

1. ServiceCollection只是List之上的包装器:

  public class ServiceCollection : IServiceCollection { private List _descriptors = new List(); } 

2.注册服务时,会在列表中添加新的描述符:

  private static IServiceCollection Add( IServiceCollection collection, Type serviceType, Type implementationType, ServiceLifetime lifetime) { var descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime); collection.Add(descriptor); return collection; } 

因此,可以在此列表中添加/删除描述符以替换注册:

 IFoo service = services.BuildServiceProvider().GetService(); Assert.True(service is FooA); var descriptor = services.FirstOrDefault(d => d.ServiceType == typeof(IFoo)); Assert.NotNull(descriptor); services.Remove(descriptor); service = services.BuildServiceProvider().GetService(); Assert.Null(service); 

我们用Replace方法完成:

 services.Replace(ServiceLifetime.Transient); 

它的实施:

 public static IServiceCollection Replace( this IServiceCollection services, ServiceLifetime lifetime) where TService : class where TImplementation : class, TService { var descriptorToRemove = services.FirstOrDefault(d => d.ServiceType == typeof(TService)); services.Remove(descriptorToRemove); var descriptorToAdd = new ServiceDescriptor(typeof(TService), typeof(TImplementation), lifetime); services.Add(descriptorToAdd); return services; }