.NET Core DI使用自定义委托解析键控服务将返回null [英] .NET Core DI resolving keyed services using custom delegate returns null

查看:104
本文介绍了.NET Core DI使用自定义委托解析键控服务将返回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 这样设置 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 之后在 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆