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

.NET5环境下Hangfire调用DI注入的WCF客户端代理出现故障态问题

问题根因

你注册WCF客户端的代码存在本质错误:预先创建了一个CustomerUpdateStarTransportPortTypesClient全局实例,再通过AddTransient返回该固定实例,即使标注了Transient生命周期,实际容器每次返回的都是同一个单例实例。WCF客户端代理不是线程安全的,多线程并发调用或者上一次调用出错进入故障状态后,后续所有调用都会直接报错,这就是你碰到故障状态报错的核心原因。

兼容DI的修复方案

1. 修正服务注册逻辑

要实现真正的Transient生命周期,每次获取客户端都新建实例,把实例创建逻辑放到注册委托内部:

public static IServiceCollection AddCustomerUpdateClient(this IServiceCollection services, IConfigurationRoot config)
{
    ServicePointManager.Expect100Continue = true;
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
            | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
    // 绑定配置是固定的,可以复用不需要每次创建
    BasicHttpsBinding binding = new BasicHttpsBinding()
    {
        SendTimeout = TimeSpan.FromMinutes(1),
        OpenTimeout = TimeSpan.FromMinutes(1),
        CloseTimeout = TimeSpan.FromMinutes(1),
        ReceiveTimeout = TimeSpan.FromMinutes(10),
        
        Namespace = "http://www.starstandards.org/webservices/2005/10/transport",
        TextEncoding = System.Text.Encoding.UTF8,
        UseDefaultWebProxy = true,
        
    };
    binding.MaxReceivedMessageSize = 50000000;
    binding.ReaderQuotas.MaxArrayLength = 50000000;
    binding.ReaderQuotas.MaxStringContentLength = 50000000;
    binding.ReaderQuotas.MaxNameTableCharCount = 50000000;
    var endpointConfig = config.GetSection(nameof(EndPointConfig));
    EndpointAddress endpointAddress = new EndpointAddress($"{endpointConfig[nameof(EndPointConfig.CuUrl)]}");
    
    // 把客户端创建放到委托内,每次请求都新建实例
    services.AddTransient(x => new CustomerUpdateStarTransportPortTypesClient(binding, endpointAddress));
    return services;
}

2. 增加WCF客户端状态校验与异常处理

WCF客户端调用前需要判断状态,故障状态直接销毁重建,调用结束后正确释放资源,同时修正OperationContextScope的异步使用问题:
修改CustomerUpdateService代码如下:

public class CustomerUpdateService : StarService, ICustomerUpdateService
{
    private readonly IServiceProvider _serviceProvider;

    // 改为注入IServiceProvider,每次需要客户端时主动获取新实例
    public CustomerUpdateService(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    [OperationContract]
    public async Task<AcknowledgeCustomerInformationPayload> ProcessCustomerUpdateAsync(
        ProcessCustomerInformationPayload customerPayload,
        string siteCode,
        ApplicationSetttings appSettings,
        string token,
        string customerName
        )
    {
        var fordHeader = new FordDealerIdentity()
        {
            SiteCode = siteCode
        };

        DataContractSerializer dcs = new DataContractSerializer(typeof(FordDealerIdentity));
        // 每次调用从容器拿新的客户端实例
        var starClient = _serviceProvider.GetRequiredService<CustomerUpdateStarTransportPortTypesClient>();
        try
        {
            if (starClient.State == CommunicationState.Faulted)
            {
                starClient.Abort();
                // 故障则重新获取实例
                starClient = _serviceProvider.GetRequiredService<CustomerUpdateStarTransportPortTypesClient>();
            }

            using (OperationContextScope scope = new OperationContextScope(starClient.InnerChannel))
            {
                AddHeaders(appSettings, fordHeader, dcs, token);
                var message = new ProcessMessage()
                {
                    payload = customerPayload
                };
                // 把await放到using块内部,避免OperationContext异步丢失
                var result = await starClient.ProcessMessageAsync(null, message);
                return result.ProcessMessageResponse.payload;
            }
        }
        catch (CommunicationException ex)
        {
            starClient.Abort();
            throw;
        }
        finally
        {
            if (starClient.State == CommunicationState.Opened)
            {
                starClient.Close();
            }
            else if (starClient.State == CommunicationState.Faulted)
            {
                starClient.Abort();
            }
        }
    }
}

3. 可选高性能复用方案

如果要避免每次调用都新建客户端的性能损耗,可以实现线程安全的WCF代理池服务,每次从池里取可用实例,故障实例直接销毁,使用完成后归还到池。该方案同样完全兼容DI规范,适合调用量较高的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 17:24:04