WCF调用第三方Web Service报parameters元素类型冲突求解
WCF服务调用XML类型映射冲突问题及解决方案
问题描述
- 通过Visual Studio导入第三方WCF Web Service后自动生成服务接口代码(Reference.cs),调用
AuthenticateJAAS服务时抛出InvalidOperationException异常 - 异常详情:空命名空间
''下的顶级XML元素parameters同时引用XAFProject.Module.WS.MCWFUsers.mcwfUsersGetUserGroupsIn和XAFProject.Module.WS.MCWFUsers.mcwfUsersAuthenticateJAASIn两种不同类型,提示需使用XML特性为元素或类型指定其他XML名称或命名空间 - 约束条件:服务为第三方提供,WSDL不可修改,无法调整
parameters元素的名称或所属命名空间 - 验证现象:如果从Reference.cs中移除
GetUserGroupsRequest类及关联代码,AuthenticateJAAS服务可正常调用,此时生成的合法SOAP请求XML如下:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <AuthenticateJAAS xmlns="http://services.senior.com.br"> <user xmlns=""/> <password xmlns=""/> <encryption xmlns="">0</encryption> <parameters xmlns=""> <flowInstanceID xsi:nil="true"/> <flowName xsi:nil="true"/> <pmUserName>******</pmUserName> <pmUserPassword>******</pmUserPassword> </parameters> </AuthenticateJAAS> </s:Body> </s:Envelope>
- 原有客户端调用代码如下:
sapiens_SyncMCWFUsersClient mCWFUsersClient = new sapiens_SyncMCWFUsersClient(); mcwfUsersAuthenticateJAASIn parameters = new mcwfUsersAuthenticateJAASIn(); parameters.pmUserName = "******"; parameters.pmUserPassword = "******"; parameters.pmEncrypted = 0; AuthenticateJAASRequest request = new AuthenticateJAASRequest("", "", 0, parameters); AuthenticateJAASResponse response = mCWFUsersClient.AuthenticateJAAS(request);
- 前期预判思路:考虑通过命名空间前缀处理类型映射冲突,不确定可行性。
可行解决方案
注意:通过命名空间前缀处理冲突的方案不可行。XML命名空间前缀只是命名空间的别名,不会改变元素实际所属的命名空间,无法解决同命名空间下同元素名的类型映射冲突,无需在该方向投入调试时间。
- 方案1:自定义操作序列化行为,关闭全局类型共享
冲突根因是WCF默认的XmlSerializer会全局注册所有操作用到的元素-类型映射,两个操作下都存在无命名空间的parameters元素,就会出现映射冲突。只需要给两个冲突的操作单独配置序列化行为,让序列化器在处理对应操作时仅加载当前操作需要的类型映射,不做全局共享即可解决问题,不需要修改自动生成的Reference.cs代码。
实现代码如下,在客户端初始化后、发起调用前插入配置逻辑即可:
sapiens_SyncMCWFUsersClient mCWFUsersClient = new sapiens_SyncMCWFUsersClient(); var endpointContract = mCWFUsersClient.Endpoint.Contract; // 遍历所有操作,为两个冲突的操作替换自定义序列化行为 foreach (var op in endpointContract.Operations) { if (op.Name is "AuthenticateJAAS" or "GetUserGroups") { // 移除默认挂载的序列化行为 var defaultBehavior = op.Behaviors.Find<XmlSerializerOperationBehavior>(); if (defaultBehavior != null) op.Behaviors.Remove(defaultBehavior); // 挂载新的序列化行为,配置为操作级独立序列化,不做全局类型映射 XmlSerializerFormatAttribute formatAttr = new XmlSerializerFormatAttribute(); XmlSerializerOperationBehavior customBehavior = new XmlSerializerOperationBehavior(op, formatAttr); op.Behaviors.Add(customBehavior); } } // 原有业务调用逻辑无需修改 mcwfUsersAuthenticateJAASIn parameters = new mcwfUsersAuthenticateJAASIn(); parameters.pmUserName = "******"; parameters.pmUserPassword = "******"; parameters.pmEncrypted = 0; AuthenticateJAASRequest request = new AuthenticateJAASRequest("", "", 0, parameters); AuthenticateJAASResponse response = mCWFUsersClient.AuthenticateJAAS(request);
- 方案2:实现客户端消息检查器,手动处理序列化
如果方案1在特殊框架版本下不生效,可以实现IClientMessageInspector接口,在请求发送前绕过WCF默认的序列化逻辑,手动拼接符合服务要求的SOAP XML消息,收到响应后再手动反序列化为对应实体类。该方案兼容性最高,完全不依赖WCF的自动类型映射规则,无论WSDL中存在多少同名同命名空间的元素都不会出现冲突,缺点是需要手动处理XML构造和解析,代码量稍大。
内容的提问来源于stack exchange,提问作者Killer
相关产品推荐
相关产品推荐

