簡體   English   中英

替換 ASP.NET Core 內置 DI 容器中的服務注冊?

[英]Replace service registration in ASP.NET Core built-in DI container?

讓我們考慮在Startup.ConfigureServices的服務注冊:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IFoo, FooA>();
}

是否有可能改變IFoo登記FooBAddTransient一直叫什么名字? 它可以用於測試目的(例如,在TestStartup子類中)或者我們對代碼庫的訪問受到限制時。

如果我們注冊另一個IFoo實現:

services.AddTransient<IFoo, FooA>();
services.AddTransient<IFoo, FooB>();

然后GetService<IFoo>返回FooB而不是FooA

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

但是, GetServices<IFoo>成功返回了兩個實現(對於GetService<IEnumerable<IFoo>> ):

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

IServiceCollection合約中有Remove(ServiceDescriptor)方法。 我應該如何使用ServiceDescriptor來修改服務注冊?

使用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);

也可以看看:

如果您知道兩個簡單的事情,很容易覆蓋 ASP.NET Core DI 功能:

1. ServiceCollection只是List<ServiceDescriptor>之上的一個包裝器:

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

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<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);

我們以Replace<TService, TImplementation>擴展方法結束:

services.Replace<IFoo, FooB>(ServiceLifetime.Transient);

它的實現:

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;
}

只是添加@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;
}

如果我們想將它與實例化服務的工廠一起使用,如下例所示:

var serviceProvider = 
  new ServiceCollection()
    .Replace<IMyService>(sp => new MyService(), ServiceLifetime.Singleton)
    .BuildServiceProvider();

我替換了我的測試夾具 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));
               }));
    }

並在測試中使用它作為

var respository = new ListRepository();
var client = _factory
    .WithRepository(respository)
    .CreateClient();

在最新版本的 .net core(net5.0) 中,應該是這樣。

using Microsoft.Extensions.DependencyInjection;

services.Replace<IFoo, FooB>(ServiceLifetime.Transient); // Or ServiceLifetime.Singleton

或者試試這個。

services.Replace(ServiceDescriptor.Transient<IFoo, FooB>());

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM