web-dev-qa-db-ja.com

ASP.NET CoreビルトインDIコンテナーのサービス登録を置き換えますか?

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

IServiceCollectionコントラクトにはRemove(ServiceDescriptor)メソッドがあります。サービス登録を変更するには、ServiceDescriptorをどうすればよいですか?

29
Ilya Chumakov

これは、 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);

こちらもご覧ください:

40
Dustin Kingen

次の2つの単純なことがわかっている場合、ASP.NET Core DI機能を簡単にオーバーライドできます。

1. ServiceCollectionList<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;
}
26
Ilya Chumakov