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

时间:2017-04-24 14:26:21

标签: c# dependency-injection asp.net-core .net-core

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

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

IFoo被调用后,是否可以将FooB注册更改为AddTransient?它有助于测试目的(例如,在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);

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

4 个答案:

答案 0 :(得分:31)

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

// IFoo -> FooA
services.AddTransient<IFoo, FooA>();

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

另见:

答案 1 :(得分:21)

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

1。 ServiceCollection只是List<ServiceDescriptor>

之上的包装器
    public class ServiceCollection : IServiceCollection
    {
        private List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();
    }

2。注册服务时,新描述符为added to list

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

答案 2 :(得分:1)

只需添加@ ilya-chumakov的好答案,这是相同的方法,但支持实现工厂

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

如果我们想将其与实例化服务的工厂一起使用,例如以下示例:

{{1}}

答案 3 :(得分:-1)

在最新版本的.net core(net5.0)中,应该是这样的。

using Microsoft.Extensions.DependencyInjection;

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

或者试试这个。

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