.NET Core DI使用自定义委托解析键控服务将返回null

时间:2019-04-03 12:12:34

标签: 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();
}

然后,我在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;
        }
    });
}

然后在我的课程中,像这样注入依赖项:

private readonly Func<ServerType, IGatewayServer> _gatewayAccessor;

public GatewayServerCollection(Func<ServerType, IGatewayServer> gatewayAccessor)
{
    _gatewayAccessor = gatewayAccessor;
}

但是稍后当我在_gatewayAccessor中调用GatewayServerCollection来获取IGatewayServer的实例时,它将返回null。我这样称呼:

var server = _gatewayAccessor(ServerType.Type1);

我想念什么?

1 个答案:

答案 0 :(得分:1)

将您的注册更改为以下内容:

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或实现(分别为Server1Server2)的MS.DI中的注册。当您请求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实例被注入ScopedSingleton使用者中(但请确保validation is enabled)。如果您将其注册为Transient,则该委托将被注入Singleton使用者中,但是当请求的服务依赖于{{1 }}的生活方式,因为这会导致Captive Dependencies。否则,当您解决实现GetRequiredService的{​​{1}}组件时,甚至可能导致内存泄漏(真糟糕!)。但是,将委托注册为Scoped也会导致专属依赖性产生相同的问题。因此,Transient是唯一明智的选择。

代替返回IDisposable来获取未知的Singleton

Scoped

我抛出异常,使应用程序快速失败:

null