Как использовать ServiceCollection.Replace в инъекции зависимостей?

Это все еще не поддерживается через 3 года? Глядя на этот вопрос: .NET Core IServiceCollection Replace не обновляется

То, что я пытаюсь достичь в XamarinForms, - это замена во время выполнения зарегистрированного типа (службы данных) на основе интерфейса другим по выбору пользователя.

Я нашел функцию расширения Replace (), но не нашел способа ее уместить. Основная причина в том, что после HostBuilder.ConfigureServices () кажется, что требуется HostBuilder.Build (), но может быть вызван только один раз. Так как же можно изменить регистрацию?

Или это все еще не предназначено? Итак, в чем тогда смысл Replace ()?

Я имею в виду Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.Replace().

2 ответа

Решение

То, чего вы хотите достичь, не поддерживается. Это сделано специально, и маловероятно, что это когда-либо будет поддерживаться. Возможность замены регистраций во время выполнения приводит к большой сложности внутри реализации контейнера и может легко привести к многопоточным проблемам или просто неожиданному поведению для пользователя. Вот почему более новые версии Autofac и Ninject имеют схожую философию дизайна, когда замена существующих регистраций после сборки не допускается. Другие контейнеры DI, такие как Simple Injector, даже применяли это «ограничение» с самого начала.

Документация Simple Injector содержит более подробное объяснение того, почему он не позволяет обновлять уже «скомпилированную» конфигурацию. Хотя текст написан в контексте Simple Injector, данные примеры являются общими по своей природе и применимы для всех контейнеров DI.

Если вы хотите заменить что-то во время выполнения, вам следует вместо этого использовать реализацию прокси. Внешний вид такого прокси зависит от требований, но вот простой пример:

      services.AddTransient<ServiceA>();
services.AddTransient<ServiceB>();
services.AddTransient<IService, ProxyService>();
      public class ProxyService : IService
{
    public ProxyService(ServiceA a, ServiceB b) ...

    public void SomeMethod()
    {
        if (some condition)
            this.a.SomeMethod();
        else
            this.b.SomeMethod();
    }
}

Потратьте два дня, чтобы выяснить, надеюсь, это поможет вам

      public class MockServiceTests
{
    // keep your own service collection
    private ServiceCollection _services;
    private IWebHost _webHost = null;
    
    public MockServiceTests()
    {
        IWebHostBuilder webHostBuilder = WebHost.CreateDefaultBuilder();
        this._webHost = webHostBuilder.UseStartup<Startup>(webHostBuilderContext =>
        {
            Startup startup = new Startup(webHostBuilderContext.Configuration);
            this._services = new ServiceCollection();
            // initialize own service collection by startup
            startup.ConfigureServices(this._services);
            return startup;
        })
        .Build();
    }
        
    private T GetService<T>()
    {
        // get service instance from own service collection
        return this._services.BuildServiceProvider().GetRequiredService<T>();
    }

    [TestMethod()]
    public void Test()
    {
        // arrange
        // ...
        _services.Replace(ServiceDescriptor.Singleton(mockObj));
    
        // act
        var expect = GetService<MockObject>().GetMockObj();
    
        // assert
        Assert.AreEqual<string>(expect, mockObj);
    }
}
Другие вопросы по тегам