Измените вашу регистрацию следующим образом:
public static void AddGatewayServers(this IServiceCollection services)
{
services.AddTransient<Server1>();
services.AddTransient<Server2>();
services.AddScoped<Func<ServerType, IGatewayServer>>(provider => (key) =>
{
switch (key)
{
case ServerType.Type1: return provider.GetRequiredService<Server1>();
case ServerType.Type2: return provider.GetRequiredService<Server2>();
default: throw new InvalidEnumArgumentException(
typeof(ServerType), (int)key, nameof(key));
}
});
}
Наиболее важные изменения из этого:
services.AddTransient<IGatewayServer, Server1>();
services.AddTransient<IGatewayServer, Server2>();
К этому:
services.AddTransient<Server1>();
services.AddTransient<Server2>();
Регистрацияв MS.DI от простого отображения словаря от типа сервиса (IGatewayServer
) до реализации (Server1
или Server2
соответственно).Когда вы запрашиваете Server1
, он не может найти typeof(Server1)
в своем словаре.Поэтому решение состоит в том, чтобы зарегистрировать эти типы по их конкретному типу.
Кроме того, я использовал метод GetRequiredService
:
provider.GetRequiredService<Server1>()
Вместо GetService
:
provider.GetService<Server1>()
GetRequiredService
вызовет исключение, если регистрация не существует, что позволяет вашему коду быстро работать при сбое.
Я изменил регистрацию делегата с Transient
:
services.AddTransient<Func<ServerType, IGatewayServer>>
до Scoped
:
services.AddScoped<Func<ServerType, IGatewayServer>>
Это предотвращает его инъекцию любому потребителю Singleton
, поскольку MS.DI только предотвращает внедрение служб Scoped
в Singleton
потребителей, но не препятствует внедрению Transient
экземпляров в Scoped
или Singleton
потребителей (но убедитесь, что проверка включена ).В случае, если вы зарегистрируете его как Transient
, делегат будет внедрен в Singleton
потребителей, но это в конечном итоге приведет к сбою во время выполнения, когда вы вызываете GetRequiredService
, когда запрашиваемая служба зависит от Scoped
образа жизни, поскольку это приведет к Пленные зависимости .Или это может даже вызвать утечку памяти, когда вы решаете Transient
компонентов, которые реализуют IDisposable
(чёрт!).Однако регистрация делегата с именем Singleton
также вызовет те же проблемы с зависимостями Captive.Так что Scoped
- единственный разумный вариант.
Вместо того, чтобы возвращать null
для неизвестного ServerType
:
default:
return null;
Я выбрасываю исключение, позволяющее приложению быстро проваливаться:
default:
throw new InvalidEnumArgumentException(...);