Jin*_*ish 1 c# dependency-injection .net-core .net-core-2.2
我正在处理一个奇怪的案件。我有一个 .NET Core 控制台应用程序,其设置如下:
private static async Task Main(string[] args)
{
var runAsService = !(Debugger.IsAttached || args.Contains("console"));
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddLogging(loggingBuilder => { loggingBuilder.AddConsole(); });
services.AddGatewayServers();
services.AddHostedService<GatewayService>();
});
if (runAsService)
await builder.RunServiceAsync();
else
await builder.RunConsoleAsync();
}
Run Code Online (Sandbox Code Playgroud)
然后我IServiceCollection对此设置进行了扩展AddGatewayServers(),如下所示:
public static void AddGatewayServers(this IServiceCollection services)
{
services.AddTransient<IGatewayServer, Server1>();
services.AddTransient<IGatewayServer, Server2>();
services.AddTransient<Func<ServerType, IGatewayServer>>(provider => key =>
{
switch (key)
{
case ServerType.Type1: return provider.GetService<Server1>();
case ServerType.Type2: return provider.GetService<Server2>();
default: return null;
}
});
}
Run Code Online (Sandbox Code Playgroud)
然后在我的班级中我注入这样的依赖项:
private readonly Func<ServerType, IGatewayServer> _gatewayAccessor;
public GatewayServerCollection(Func<ServerType, IGatewayServer> gatewayAccessor)
{
_gatewayAccessor = gatewayAccessor;
}
Run Code Online (Sandbox Code Playgroud)
但是当我_gatewayAccessor稍后调用它GatewayServerCollection来获取它的实例时IGatewayServer,它会返回null。我这样称呼它:
var server = _gatewayAccessor(ServerType.Type1);
Run Code Online (Sandbox Code Playgroud)
我缺少什么?
将您的注册更改为以下内容:
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));
}
});
}
Run Code Online (Sandbox Code Playgroud)
最重要的变化来自于此:
services.AddTransient<IGatewayServer, Server1>();
services.AddTransient<IGatewayServer, Server2>();
Run Code Online (Sandbox Code Playgroud)
对此:
services.AddTransient<Server1>();
services.AddTransient<Server2>();
Run Code Online (Sandbox Code Playgroud)
MS.DI 中的注册来自从服务类型 ( IGatewayServer) 到实现(Server1或Server2分别)的简单字典映射。当你请求时Server1,它在它的字典中找不到typeof(Server1)。因此,解决方案是通过具体类型来注册这些类型。
最重要的是,我使用了以下方法GetRequiredService:
provider.GetRequiredService<Server1>()
Run Code Online (Sandbox Code Playgroud)
代替GetService:
provider.GetService<Server1>()
Run Code Online (Sandbox Code Playgroud)
GetRequiredService当注册不存在时会抛出异常,这允许您的代码快速失败。
我将代表的注册更改为Transient:
services.AddTransient<Func<ServerType, IGatewayServer>>
Run Code Online (Sandbox Code Playgroud)
到Scoped:
services.AddScoped<Func<ServerType, IGatewayServer>>
Run Code Online (Sandbox Code Playgroud)
这可以防止它被注入到任何Singleton消费者中,因为 MS.DI 仅阻止Scoped服务被注入到Singleton消费者中,但不会阻止Transient实例被注入到Scoped或Singleton消费者中(但请确保启用验证)。如果您将其注册为Transient,委托将被注入到消费者中,但是当您在请求的服务依赖于生活方式时调用时,Singleton这最终会在运行时失败,因为这会导致Captive Dependencies。或者,当您解析实现的组件时,它甚至可能导致内存泄漏(恶心!)。但是,将委托注册为也会导致与强制依赖项相同的问题。所以这是唯一明智的选择。GetRequiredServiceScopedTransientIDisposableSingletonScoped
而不是返回null未知的ServerType:
default:
return null;
Run Code Online (Sandbox Code Playgroud)
我抛出一个异常,让应用程序快速失败:
default:
throw new InvalidEnumArgumentException(...);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2112 次 |
| 最近记录: |