[英]Replace service registration in ASP.NET Core built-in DI container?
Let us consider a service registration in Startup.ConfigureServices
:让我们考虑在
Startup.ConfigureServices
的服务注册:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IFoo, FooA>();
}
Is it possible to change IFoo
registration to FooB
after AddTransient
has been called?是否有可能改变
IFoo
登记FooB
后AddTransient
一直叫什么名字? It can be helpful for testing purposes (for example, in TestStartup
subclass) or if our access to codebase is limited.它可以用于测试目的(例如,在
TestStartup
子类中)或者我们对代码库的访问受到限制时。
If we register another IFoo
implementation:如果我们注册另一个
IFoo
实现:
services.AddTransient<IFoo, FooA>();
services.AddTransient<IFoo, FooB>();
Then GetService<IFoo>
returns FooB
instead of FooA
:然后
GetService<IFoo>
返回FooB
而不是FooA
:
IFoo service = services.BuildServiceProvider().GetService<IFoo>();
Assert.True(service is FooB);
However, GetServices<IFoo>
successfully returns both implementations (and the same for GetService<IEnumerable<IFoo>>
):但是,
GetServices<IFoo>
成功返回了两个实现(对于GetService<IEnumerable<IFoo>>
):
var list = services.BuildServiceProvider().GetServices<IFoo>().ToList();
Assert.Equal(2, list.Count);
There is Remove(ServiceDescriptor)
method in IServiceCollection
contract. IServiceCollection
合约中有Remove(ServiceDescriptor)
方法。 What should I do with ServiceDescriptor
to modify a service registration?我应该如何使用
ServiceDescriptor
来修改服务注册?
This is simple using the Replace(IServiceCollection, ServiceDescriptor)
method from the ServiceCollectionDescriptorExtensions
class.使用
ServiceCollectionDescriptorExtensions
类中的Replace(IServiceCollection, ServiceDescriptor)
方法很简单。
// IFoo -> FooA
services.AddTransient<IFoo, FooA>();
// Replace
// IFoo -> FooB
var descriptor =
new ServiceDescriptor(
typeof(IFoo),
typeof(FooB),
ServiceLifetime.Transient);
services.Replace(descriptor);
See also:也可以看看:
It is easy to override ASP.NET Core DI functionality if you know two simple things:如果您知道两个简单的事情,很容易覆盖 ASP.NET Core DI 功能:
ServiceCollection
is just a wrapper on top of List<ServiceDescriptor>
: ServiceCollection
只是List<ServiceDescriptor>
之上的一个包装器: public class ServiceCollection : IServiceCollection
{
private List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();
}
private static IServiceCollection Add(
IServiceCollection collection,
Type serviceType,
Type implementationType,
ServiceLifetime lifetime)
{
var descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime);
collection.Add(descriptor);
return collection;
}
Therefore, it is possible to add/remove descriptors to/from this list to replace the registration:因此,可以在此列表中添加/删除描述符以替换注册:
IFoo service = services.BuildServiceProvider().GetService<IFoo>();
Assert.True(service is FooA);
var descriptor = services.FirstOrDefault(d => d.ServiceType == typeof(IFoo));
Assert.NotNull(descriptor);
services.Remove(descriptor);
service = services.BuildServiceProvider().GetService<IFoo>();
Assert.Null(service);
We finish with Replace<TService, TImplementation>
extention method:我们以
Replace<TService, TImplementation>
扩展方法结束:
services.Replace<IFoo, FooB>(ServiceLifetime.Transient);
Its implementation:它的实现:
public static IServiceCollection Replace<TService, TImplementation>(
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;
}
Just to add on @ilya-chumakov 's great answer, here is the same method but with support for implementation factories只是添加@ilya-chumakov 的好答案,这里是相同的方法,但支持实现工厂
public static IServiceCollection Replace<TService>(
this IServiceCollection services,
Func<IServiceProvider, TService> implementationFactory,
ServiceLifetime lifetime)
where TService : class
{
var descriptorToRemove = services.FirstOrDefault(d => d.ServiceType == typeof(TService));
services.Remove(descriptorToRemove);
var descriptorToAdd = new ServiceDescriptor(typeof(TService), implementationFactory, lifetime);
services.Add(descriptorToAdd);
return services;
}
in case we want to use it with a factory that instantiates the service like the following sample:如果我们想将它与实例化服务的工厂一起使用,如下例所示:
var serviceProvider =
new ServiceCollection()
.Replace<IMyService>(sp => new MyService(), ServiceLifetime.Singleton)
.BuildServiceProvider();
I replace the repository in my Test Fixture WebApplicationFactory using我替换了我的测试夹具 WebApplicationFactory 中的存储库使用
public static WebApplicationFactory<TEntryPoint> WithRepository<TEntryPoint>(
this WebApplicationFactory<TEntryPoint> webApplicationFactory,
IStoreRepository storeRespository)
where TEntryPoint : class
{
return webApplicationFactory.WithWebHostBuilder(builder => builder.ConfigureTestServices(
services =>
{
services.Replace(ServiceDescriptor.Scoped(p => storeRespository));
}));
}
And use it in the test as并在测试中使用它作为
var respository = new ListRepository();
var client = _factory
.WithRepository(respository)
.CreateClient();
In the latest version of .net core(net5.0), this is how it should be.在最新版本的 .net core(net5.0) 中,应该是这样。
using Microsoft.Extensions.DependencyInjection;
services.Replace<IFoo, FooB>(ServiceLifetime.Transient); // Or ServiceLifetime.Singleton
Or try this one.或者试试这个。
services.Replace(ServiceDescriptor.Transient<IFoo, FooB>());
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.