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

ASP.NET Core中用工厂模式构造带数值参数的gRPC拦截器

解决方案与建议

一、更优雅的键控服务注册方案

ASP.NET Core 3.1+ 提供的键控服务(Keyed Services) 正好适配你这种需要按参数区分实例的场景,完全可以替代自定义工厂池的实现,更符合框架原生设计:

1. 注册带键的拦截器单例

直接通过AddKeyedSingleton为不同参数值的MyInterceptor注册带标识的单例:

// 注册参数为1的拦截器,键直接使用参数值作为标识
builder.Services.AddKeyedSingleton<MyInterceptor>(1, sp => new MyInterceptor(1));
// 注册参数为10的拦截器
builder.Services.AddKeyedSingleton<MyInterceptor>(10, sp => new MyInterceptor(10));

2. 为gRPC客户端绑定对应键的拦截器

添加拦截器时,通过服务提供器的GetRequiredKeyedService方法按键获取实例:

builder.Services
    .AddGrpcClient<MyService.MyServiceClient>()
    .AddInterceptor(sp => sp.GetRequiredKeyedService<MyInterceptor>(10));

这种方式的优势:

  • 无需自定义工厂类,由DI容器统一管理实例生命周期与复用
  • 代码更简洁,符合ASP.NET Core的原生开发模式
  • 天然支持线程安全,DI容器的单例实例本身就是线程安全的(只要你的MyInterceptor是线程安全的)

如果你的拦截器参数是动态生成的,也可以结合工厂模式与键控服务,比如注册一个键控工厂来动态创建,但对于固定参数值的场景,直接注册带键的单例是最优解。

二、关于Dictionary与ConcurrentDictionary的选择

你的自定义MyInterceptorPool中必须使用ConcurrentDictionary,原因如下:

  • ASP.NET Core应用在运行时是多线程环境,多个请求线程可能同时调用GetOrAddMyInterceptor方法
  • Dictionary并非线程安全,并发执行TryGetValue+Add会导致数据竞争,可能抛出异常或出现数据损坏
  • ConcurrentDictionary的GetOrAdd方法是原子操作,天然支持线程安全,且代码更简洁

优化后的MyInterceptorPool实现:

public class MyInterceptorPool
{
    private readonly ConcurrentDictionary<int, MyInterceptor> _pool = new();

    public MyInterceptor GetOrAddMyInterceptor(int parameter)
    {
        // 直接使用ConcurrentDictionary的原子GetOrAdd方法
        return _pool.GetOrAdd(parameter, p => new MyInterceptor(p));
    }
}

总结

优先推荐使用键控服务方案,它是框架原生支持的特性,无需额外维护自定义池;如果必须使用自定义工厂池,一定要替换为ConcurrentDictionary保证线程安全。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 21:40:16