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

C#实现泛型方法实例化Praxedo WSDL生成的manager client及报错解决

问题根因分析

  • 首先是构造函数参数类型不匹配:WSDL自动生成的ClientBase<TChannel>子类的带参构造函数接收的参数是(Binding, EndpointAddress),你当前代码中传递的第二个参数是Uri类型,无法匹配到对应构造函数,因此抛出MissingMethodException
  • 其次是多余的new()约束:你的泛型约束中加了new()要求,但是你全程没有使用无参构造函数实例化客户端,该约束不仅无用,还会强制要求所有客户端类必须实现无参构造(无参构造依赖配置文件中的端点配置,对你的场景完全没必要)
  • 额外隐藏问题:Authorize方法中OperationContextScope没有用using包裹,你当前的写法创建的上下文在方法返回后就会被释放,后续调用服务接口时无法携带授权头,会出现未授权错误。

修正后的代码调整

1. 工厂类修改

using PraxedoIntegration.Common.Configurations;
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using WcfCoreMtomEncoder;

namespace PraxedoV6.ManagerClientWrappers.Helpers
{
    public class PraxedoClientFactory
    {
        private readonly PraxedoSettings _praxedoSettings;

        public PraxedoClientFactory(PraxedoSettings praxedoSettings)
            => _praxedoSettings = praxedoSettings ?? throw new ArgumentNullException(nameof(praxedoSettings));

        // 去掉new()约束,参数endpoint改为EndpointAddress类型
        public T Create<T, TChannel>(EndpointAddress endpoint, HttpsTransportBindingElement bindingElement)
            where T : ClientBase<TChannel>
            where TChannel : class
        {
            var encoding = CreateMtomMessageEncoderBindingElement();
            var customBinding = new CustomBinding(encoding, bindingElement);

            // 参数类型匹配对应构造函数,实例化客户端
            var managerClient = (T)Activator.CreateInstance(typeof(T), customBinding, endpoint);
            
            _praxedoSettings.AddAuthorizationTo(managerClient);
            return managerClient;
        }

        private static MtomMessageEncoderBindingElement CreateMtomMessageEncoderBindingElement()
            => new(new TextMessageEncodingBindingElement
            {
                MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None)
            });

        // 去掉new()约束,将授权逻辑改为调用时执行,保证上下文生效
        // 无返回值调用重载
        public void ExecuteWithAuth<T, TChannel>(T client, Action<T> execute)
            where T : ClientBase<TChannel>
            where TChannel : class
        {
            using var scope = new OperationContextScope(client.InnerChannel);
            OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name]
                = _praxedoSettings.ToHttpRequestMessageProperty();
            execute(client);
        }

        // 有返回值调用重载
        public TResult ExecuteWithAuth<T, TChannel, TResult>(T client, Func<T, TResult> execute)
            where T : ClientBase<TChannel>
            where TChannel : class
        {
            using var scope = new OperationContextScope(client.InnerChannel);
            OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name]
                = _praxedoSettings.ToHttpRequestMessageProperty();
            return execute(client);
        }
    }
}

2. 调用方式调整

_customerManagerClient = praxedoClientFactory.Create<CustomerManagerClient, CustomerManagerV6.CustomerManager>(
    // 将Uri转为EndpointAddress
    new EndpointAddress(_praxedoSettings.CustomerManagerEndpoint),
    new HttpsTransportBindingElement()
);

// 后续调用服务接口示例:
var customer = praxedoClientFactory.ExecuteWithAuth(_customerManagerClient, client => client.GetCustomer(customerId));

补充说明

  • 你原本的泛型参数设计方向是正确的,T为客户端实现类,TChannel为服务接口,该约束可以覆盖所有WSDL生成的同类型客户端类,你的需求完全可以实现,调整后可以复用在所有6个ManagerClient的实例化场景中。
  • 如果你不想修改调用时传参,也可以保留Create方法的Uri参数,在方法内部自行转为EndpointAddress即可:var endpointAddress = new EndpointAddress(endpoint);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 16:24:02