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
相关产品推荐
相关产品推荐

