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

我正在为一个奇怪的案例而苦苦挣扎。我有一个 .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);

我错过了什么?


三国纷争
浏览 69回答 1
1回答

aluckdog

将您的注册更改为以下内容:public static void AddGatewayServers(this IServiceCollection services){&nbsp; &nbsp; services.AddTransient<Server1>();&nbsp; &nbsp; services.AddTransient<Server2>();&nbsp; &nbsp; services.AddScoped<Func<ServerType, IGatewayServer>>(provider => (key) =>&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; switch (key)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case ServerType.Type1: return provider.GetRequiredService<Server1>();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case ServerType.Type2: return provider.GetRequiredService<Server2>();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; default: throw new InvalidEnumArgumentException(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; typeof(ServerType), (int)key, nameof(key));&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; });}最重要的变化来自于: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时,这最终会在运行时失败,因为这会导致Captive Dependencies。或者它甚至可能导致内存泄漏,当您解析实现的组件时(糟糕!)。将代表注册为GetRequiredServiceScopedTransientIDisposableSingleton,但是,也会导致与俘虏依赖项相同的问题。所以Scoped是唯一明智的选择。而不是返回null一个未知数ServerType:default:&nbsp; &nbsp; return null;我抛出一个异常,让应用程序快速失败:default:&nbsp; &nbsp; throw new InvalidEnumArgumentException(...);
打开App,查看更多内容
随时随地看视频慕课网APP