Как использовать 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);
}
}