简体   繁体   English

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

[英]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登记FooBAddTransient一直叫什么名字? 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 功能:

1. ServiceCollection is just a wrapper on top of List<ServiceDescriptor> : 1. ServiceCollection只是List<ServiceDescriptor>之上的一个包装器:

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

2. When a service is registered, a new descriptor is added to 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;
    }

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.

相关问题 如何在 ASP.NET Core 内置 DI 容器中覆盖和接口并调用“基础”接口? - How to override and interface and call "base" interface in ASP.NET Core built-in DI container? ASP.NET 5和内置DI容器的每请求范围 - Per-request scope with ASP.NET 5 and built-in DI container ASP.NET Core XUnit 用参数替换服务 (DI) - ASP.NET Core XUnit replace service with parameter (DI) 使用带有类型化参数的.NET Core内置DI替换Ninject - Replace Ninject with .NET Core built-in DI with typed parameters 如何在 ASP.NET Core 集成测试中覆盖来自其他容器的 DI 注册 - How to override DI registration from other container in ASP.NET Core integration test ASP.NET Core:带条件的DI服务 - ASP.NET Core: DI Service with conditions 使用 ASP.NET Core 内置 IoC 容器注入多个客户端而不是一个 - Injecting multiple clients instead of just one using ASP.NET Core built-in IoC container 如何从 Asp.Net Core 的 ConfigureServices 方法中访问添加到 DI 容器的服务 - How to access a service added to DI container from inside Asp.Net Core's ConfigureServices method 在.NET Core内置DI容器中为AddScoped服务实现自定义处理 - Implement custom dispose for AddScoped services in .NET Core built-in DI Container 如何测试asp.net core内置的Ilogger - How to test asp.net core built-in Ilogger
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM