You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core如何注入同时为单例托管服务的IEnumerable<Interface>集合

问题根源

你第二种写法失效的核心原因:AddHostedService<T> 本质是将T作为IHostedService的实现注册到容器,多次注册不同服务类型时会生成独立实例,所以你注册的托管服务实例和IEnumerable<IConnectionListener>注入拿到的实例不是同一个,自然无法正常工作。
第一种手动new实例的方式虽然保证了实例唯一,但由于实例是你手动构造的,没有交给DI容器管理生命周期,容器默认不会自动调用其Dispose/DisposeAsync方法,存在资源泄漏风险。

正确实现方案

第一步:编写通用注册扩展方法

给IServiceCollection写扩展方法,一次注册就完成「单例实例+托管服务+接口暴露」三个动作,确保实例唯一且容器自动管理生命周期:

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddHostedServiceAs<TService, TInterface>(this IServiceCollection services)
        where TService : class, TInterface, IHostedService
        where TInterface : class
    {
        // 先注册单例服务本身
        services.AddSingleton<TService>();
        // 作为IHostedService注册,复用上面的单例实例
        services.AddHostedService(sp => sp.GetRequiredService<TService>());
        // 作为目标接口注册,复用上面的单例实例
        services.AddSingleton<TInterface>(sp => sp.GetRequiredService<TService>());
        return services;
    }
}

第二步:修正服务注册逻辑

直接用上面的扩展方法替换原来的注册代码即可,一行代码完成一个监听器的注册,容器自动处理生命周期、启停、注入逻辑:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureServices((hc, services) => 
        {
            // 一行注册一个监听器,同时作为托管服务和IConnectionListener暴露
            services.AddHostedServiceAs<ConnectionListener, IConnectionListener>();
            services.AddHostedServiceAs<SslConnectionListener, IConnectionListener>();
            // 注册Server托管服务
            services.AddHostedService<Server>();
        });

注意事项

你原代码里存在拼写错误:IConnectionListner少写了字母e,修正为IConnectionListener即可避免类型找不到的问题。按上述方案实现后,所有服务的构造、释放、启停都完全由DI容器托管,Server构造函数注入的IEnumerable<IConnectionListener就是正在运行的托管服务实例,事件绑定逻辑完全生效。

内容的提问来源于stack exchange,提问作者uriDium

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 03:54:02