.NET Core DI使用自定义委托解析键控服务将返回null [英] .NET Core DI resolving keyed services using custom delegate returns null
问题描述
我正在为一个奇怪的案件而苦苦挣扎。我有一个.NET Core控制台应用程序,其设置如下:
I am struggling with a weird case. I have a .NET Core console application that is setup like this:
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();
}
然后我在 IServiceCollection $ c上有扩展名$ c>这样设置
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;
}
});
}
然后在我的课程中,我像这样注入依赖项:
And then in my class I inject the dependency like this:
private readonly Func<ServerType, IGatewayServer> _gatewayAccessor;
public GatewayServerCollection(Func<ServerType, IGatewayServer> gatewayAccessor)
{
_gatewayAccessor = gatewayAccessor;
}
但是当我呼叫 _gatewayAccessor $ c时$ c>之后在
GatewayServerCollection
中获得 IGatewayServer
的实例,它返回 null
。我这样称呼它:
But when I call the _gatewayAccessor
later on in GatewayServerCollection
to get me an instance of the IGatewayServer
it returns null
. I call it like:
var server = _gatewayAccessor(ServerType.Type1);
我想念什么?
推荐答案
将注册更改为以下内容:
Change your registration to the following:
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>();
从服务类型( IGatewayServer
)实现(分别为 Server1
或 Server2
)。当您请求 Server1
时,它在其字典中找不到 typeof(Server1)
。因此,解决方案是按具体类型注册这些类型。
The registrations in MS.DI from a simple dictionary mapping from a service type (IGatewayServer
) to an implementation (Server1
or Server2
respectively). When you request Server1
, it can't find typeof(Server1)
in its dictionary. The solution, therefore, is to register those types by their concrete type.
最重要的是,我使用了 GetRequiredService
方法:
On top of that, I made use of the GetRequiredService
method:
provider.GetRequiredService<Server1>()
而不是 GetService
:
provider.GetService<Server1>()
GetRequiredService
将在注册不存在时引发异常,这会使您的代码快速执行失败。
GetRequiredService
will throw an exception when a registration does not exist, which allows your code to fail-fast.
我更改了 Transient
的委托:
services.AddTransient<Func<ServerType, IGatewayServer>>
到 Scoped
:
services.AddScoped<Func<ServerType, IGatewayServer>>
这可以防止将其注入任何 Singleton
消费者,因为MS.DI仅阻止 Scoped
服务注入到 Singleton
消费者中,但不能阻止 Transient
实例注入到 Scoped
或 Singleton
使用者中(但确保)。如果您将其注册为 Transient
,则该委托将被注入到 Singleton
使用者中,但这最终将在运行时失败当您调用 GetRequiredService
时,请求的服务取决于 Scoped
生活方式,因为这会导致圈养依赖性。或者,当您解决实现 IDisposable
的 Transient
组件时,这甚至可能导致内存泄漏(糟糕!)。但是,将委托注册为 Singleton
也会导致同样的问题。因此, Scoped
是唯一明智的选择。
This prevents it from being injected into any Singleton
consumer, as MS.DI only prevents Scoped
services to be injected into Singleton
consumers, but does not prevent Transient
instances from being injected into Scoped
or Singleton
consumers (but do make sure validation is enabled). In case you register it as Transient
, the delegate would be injected into Singleton
consumers, but this would eventually fail at runtime when you call GetRequiredService
when the requested service depends on a Scoped
lifestyle, as that would cause Captive Dependencies. Or it could even cause memory leaks, when you resolve Transient
components that implement IDisposable
(yuck!). Registering the delegate as Singleton
, however, would also cause the same issues with Captive Dependencies. So Scoped
is the only sensible option.
而不是返回 null
用于未知的 ServerType
:
Instead of returning null
for an unknown ServerType
:
default:
return null;
我抛出异常,使应用程序快速失败:
I throw an exception, allowing the application to fail fast:
default:
throw new InvalidEnumArgumentException(...);
这篇关于.NET Core DI使用自定义委托解析键控服务将返回null的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!