У меня есть две разные реализации для одного интерфейса, которые мне нужно зарегистрировать во внедрении зависимостей, и мне нужно условно проверить, какую реализацию создавать в данный момент.
Я использовал шаблон фабрики и делегат фабрики, и я просто хочу знать, есть ли лучший способ добиться этого.
Я буду использовать простые объекты, чтобы упростить задачу
IExampleService
internal interface IExampleService
{
public ExampleMode Mode { get; }
void Show();
}
internal enum ExampleMode
{
ExampleA,
ExampleB
}
ExampleAService
public ExampleMode Mode => ExampleMode.ExampleA;
public ExampleAService()
{
Console.WriteLine("Creating Example A");
}
public void Dispose()
{
Console.WriteLine("Disposing Example A");
}
public void Show()
{
Console.WriteLine("Example A Show");
}
ExampleBService
public ExampleMode Mode => ExampleMode.ExampleB;
public ExampleBService()
{
Console.WriteLine("Creating Example B");
}
public void Dispose()
{
Console.WriteLine("Disposing Example B");
}
public void Show()
{
Console.WriteLine("Example B Show");
}
Каков подходящий способ добиться выбора условной реализации при внедрении зависимостей.
Я использовал две разные реализации шаблона фабрики, первая использует Ienumerable, поэтому службы создаются DI и соответствуют назначенному зарегистрированному сроку службы, но я хочу избежать накладных расходов на создание экземпляров всех реализаций только для выбора одной.
Чтобы решить эту проблему, я использовал другой вариант фабрики, где я вручную создаю экземпляр требуемой службы но теперь мне нужно убедиться, что я удаляю этот объект вручную и эти службы больше не соответствуют зарегистрированному сроку службы службы.
ExampleServiceFactory
internal class ExampleServiceFactory : IExampleServiceFactory
{
private readonly IEnumerable exampleServices;
public ExampleServiceFactory(IEnumerable exampleServices)
{
this.exampleServices = exampleServices;
}
public IExampleService GetExampleService(ExampleMode mode)
{
//Using DI to inject Ienumerable of services
return exampleServices.FirstOrDefault(x => x.Mode == mode);
//Manually instantiate Services
//return mode switch
//{
// ExampleMode.ExampleA => new ExampleAService(),
// ExampleMode.ExampleB => new ExampleBService(),
// _ => throw new NotSupportedException(),
//};
}
}
и внедрение IServiceProvider в фабрику имело для меня смысл, но, похоже, это антишаблон
Еще один вариант локатора сервисов, которого следует избегать, — это внедрение фабрики, которая разрешает зависимости во время выполнения. Обе эти практики сочетают в себе стратегии инверсии управления.
в настоящее время я использую фабричный делегат для условного создания экземпляра требуемой реализации и передачи ее в оболочку, которую я могу свободно внедрять на другие страницы, в контроллер и т. д.
Program.cs
builder.Services.AddKeyedSingleton("ExampleA");
builder.Services.AddKeyedTransient("ExampleB");
builder.Services.AddTransient();
builder.Services.AddTransient();
//var exampleMode = ConfigurationManager.AppSettings["Device"];
builder.Services.AddTransient((ctx) =>
{
var configProvider = ctx.GetRequiredService();
var exampleMode = configProvider.GetExampleMode();
var service = exampleMode switch
{
ExampleMode.ExampleA => ctx.GetRequiredKeyedService("ExampleA"),
ExampleMode.ExampleB => ctx.GetRequiredKeyedService("ExampleB"),
_ => throw new InvalidOperationException()
};
return new GenericExampleService(service);
});
Подробнее здесь: https://stackoverflow.com/questions/782 ... -injection