WCF 从 XML 接收空模型

Posted

技术标签:

【中文标题】WCF 从 XML 接收空模型【英文标题】:WCF receives empty model from XML 【发布时间】:2018-11-22 20:32:47 【问题描述】:

我们有一个定义类的 WCF 服务(类是使用 http://xmltocsharp.azurewebsites.net/ 从 XML 自动生成的):

namespace CRMtoQLM.DAL

    [Serializable]
    [DataContract(Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
    public class Envelope
    
    [XmlElement(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
    [DataMember]
    public Body Body  get; set; 
    [XmlAttribute(AttributeName = "soapenv", Namespace = "http://www.w3.org/2000/xmlns/")]
    [DataMember]
    public string Soapenv  get; set; 
    [XmlAttribute(AttributeName = "xsd", Namespace = "http://www.w3.org/2000/xmlns/")]
    [DataMember]
    public string Xsd  get; set; 
    [XmlAttribute(AttributeName = "xsi", Namespace = "http://www.w3.org/2000/xmlns/")]
    [DataMember]
    public string Xsi  get; set; 


//[Serializable]
[XmlRoot(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
[DataContract(Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class Body

    [XmlElement(ElementName = "notifications", Namespace = "http://soap.sforce.com/2005/09/outbound")]
    [DataMember]
    public Notifications Notifications  get; set; 


//[Serializable]
[XmlRoot(ElementName = "notifications", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[DataContract(Namespace = "http://soap.sforce.com/2005/09/outbound")]
public class Notifications

    [XmlElement(ElementName = "OrganizationId", Namespace = "http://soap.sforce.com/2005/09/outbound")]
    [DataMember]
    public string OrganizationId  get; set; 
    [XmlElement(ElementName = "ActionId", Namespace = "http://soap.sforce.com/2005/09/outbound")]
    [DataMember]
    public string ActionId  get; set; 
    [XmlElement(ElementName = "SessionId", Namespace = "http://soap.sforce.com/2005/09/outbound")]
    [DataMember]
    public string SessionId  get; set; 
    [XmlElement(ElementName = "EnterpriseUrl", Namespace = "http://soap.sforce.com/2005/09/outbound")]
    [DataMember]
    public string EnterpriseUrl  get; set; 
    [XmlElement(ElementName = "PartnerUrl", Namespace = "http://soap.sforce.com/2005/09/outbound")]
    [DataMember]
    public string PartnerUrl  get; set; 
    [XmlElement(ElementName = "Notification", Namespace = "http://soap.sforce.com/2005/09/outbound")]
    [DataMember]
    public Notification Notification  get; set; 
    [XmlAttribute(AttributeName = "xmlns")]
    [DataMember]
    public string Xmlns  get; set; 


//[Serializable]
[XmlRoot(ElementName = "Notification", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[DataContract(Namespace = "http://soap.sforce.com/2005/09/outbound")]
public class Notification

    [XmlElement(ElementName = "Id", Namespace = "http://soap.sforce.com/2005/09/outbound")]
    [DataMember]
    public string Id  get; set; 
    [XmlElement(ElementName = "sObject", Namespace = "http://soap.sforce.com/2005/09/outbound")]
    [DataMember]
    public SObject SObject  get; set; 


//[Serializable]
[XmlRoot(ElementName = "sObject", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[DataContract(Namespace = "http://soap.sforce.com/2005/09/outbound")]
public class SObject

    [XmlElement(ElementName = "Id", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
    [DataMember]
    public string Id  get; set; 
    [XmlElement(ElementName = "Asset_Account_City__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
    [DataMember]
    public string Asset_Account_City__c  get; set; 
    [XmlElement(ElementName = "Asset_Account_Country__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
    [DataMember]
    public string Asset_Account_Country__c  get; set; 
    [XmlElement(ElementName = "Asset_Account_Name__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
    [DataMember]
    public string Asset_Account_Name__c  get; set; 
    [XmlElement(ElementName = "Asset_Customer_ID__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
    [DataMember]
    public string Asset_Customer_ID__c  get; set; 
    [XmlElement(ElementName = "Expiration_Date__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
    [DataMember]
    public string Expiration_Date__c  get; set; 
    [XmlElement(ElementName = "License__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
    [DataMember]
    public string License__c  get; set; 
    [XmlElement(ElementName = "Reader_Code__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
    [DataMember]
    public string Reader_Code__c  get; set; 
    [XmlElement(ElementName = "Reader_Quantity__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
    [DataMember]
    public string Reader_Quantity__c  get; set; 
    [XmlAttribute(AttributeName = "type", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
    [DataMember]
    public string Type  get; set; 
    [XmlAttribute(AttributeName = "sf", Namespace = "http://www.w3.org/2000/xmlns/")]
    [DataMember]
    public string Sf  get; set; 


IService.cs 定义如下:

    [OperationContract]
    [WebInvoke(Method = "POST",
    UriTemplate = "Test",
    RequestFormat = WebMessageFormat.Xml)]
    //[XmlSerializerFormat]
    string Test(Envelope parameter);

和实施:

    public string Test(Envelope parameter)
    
        return "";
    

但 Test 函数中的“参数”包含 NULL 对象。我知道 XML 反序列化存在问题,但不知道具体在哪里。

编辑:这是我通过 Postman 发送的请求

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<notifications xmlns="http://soap.sforce.com/2005/09/outbound">
<OrganizationId>123456</OrganizationId>
<ActionId>123456</ActionId>
<SessionId>123456</SessionId>
<EnterpriseUrl>https://eu8.salesforce.com/</EnterpriseUrl>
<PartnerUrl>https://eu8.salesforce.com/</PartnerUrl>
<Notification>
<Id>123456</Id>
<sObject xsi:type="sf:Asset" xmlns:sf="urn:sobject.enterprise.soap.sforce.com">
 <sf:Id>123456</sf:Id>
 <sf:Asset_Account_City__c>123456</sf:Asset_Account_City__c>
 <sf:Asset_Account_Country__c>123456</sf:Asset_Account_Country__c>
 <sf:Asset_Account_Name__c>123456</sf:Asset_Account_Name__c>
 <sf:Asset_Customer_ID__c>123456</sf:Asset_Customer_ID__c>
 <sf:Expiration_Date__c>123456</sf:Expiration_Date__c>
 <sf:License__c>123456</sf:License__c>
 <sf:Reader_Code__c>123456</sf:Reader_Code__c>
 <sf:Reader_Quantity__c>123456</sf:Reader_Quantity__c>
</sObject>
</Notification>
</notifications>
</soapenv:Body>
</soapenv:Envelope>

【问题讨论】:

为什么要定义 SOAP 信封格式? WCF 通常会为您解包 SOAP 信封。你不应该这样做。您能否指定您正在使用的绑定?我怀疑这只是接近正常工作的地方,因为您使用的不是设计用于处理 SOAP 的 WebHttpBinding - 由 WebInvoke 属性的存在支持。如果您执行“正常” WCF,这将有效。 该类是使用 XML 示例请求中提供的链接自动生成的。是的,我使用 WebHttpBinding 是因为项目中的其他内容。 好吧,不要那样做。为此端点使用正确的绑定(可能是 BasicHttpBinding)并让您的操作合同采用Notifications,而不是Envelope。您不需要为信封定义架构,WCF 并非旨在像这样工作。 我应该如何处理模型中的所有命名空间?如果我没听错,我只需要从课堂上删除信封和正文。你能修改代码并把它作为答案吗? 【参考方案1】:

基于 SOAP 的绑定为您解包 SOAP 信封,因此您不需要定义 SOAP 信封类型。您说您正在使用的 WebHttpBinding 对 SOAP 一无所知,它期望合同类型(您的服务方法 IService.Test 的参数)将匹配整个 HTTP 正文,所以我看到了什么的逻辑你正在做,但是我认为没有必要像这样反对框架。

要解决这种情况,请做几件事:

修改您的 IService.Test 实现以采用 Notifications 类型的参数 string Test(Notifications notifications) 修改服务配置以指定基于 HTTP 的 SOAP 绑定,可能是 BasicHttpBinding &lt;endpoint address="/relativeaddress/" binding="basicHttpBinding" ... /&gt; 我认为 SOAP 绑定可能会忽略 WebInvokeAttribute,但为了安全起见,请将其删除。这对于 SOAP 绑定来说是多余的,因为 SOAP 始终是 POST。

我认为您不需要删除 EnvelopeBody 类型,但如果它们没有被引用,它们将不会做任何事情,这正是您想要的。 WCF 绑定本身就理解 SOAP 信封,不需要指定它,它是 SOAP Body 元素的内容,需要为其提供类型。

您的 XML 示例在 Notifications 下包含一个 Notification 实例,但名称暗示此子元素可能会重复 - 如果是,我认为生成的类不适用于您,因为 @987654331 @class 有一个单一的 Notification 属性-您链接到的生成器无法知道这一点,因此我不会以任何方式对其进行故障排除。我添加了Notification 元素的副本作为兄弟元素并再次运行它 - 这次它生成了一个列表成员:

[XmlElement(ElementName="Notification", Namespace="http://soap.sforce.com/2005/09/outbound")]
public List<Notification> Notification  get; set; 

您可以使用 Visual Studio 提供的 xsd.exe 工具(我认为)生成类 - 请参阅 the documentation。如果该网络工具在幕后推动这一点,我不会感到惊讶,但我相信 xsd.exe 会生成最符合 WCF 期望的类。

【讨论】:

非常感谢您的宝贵时间。我将尝试所有建议的解决方案并报告。还有一个问题。我应该在课堂上留下哪些注释? [XmlElement]、[DataMember]、[Serializable]? 那里可能有问题。 Notifications 仅包含一个单一的 Notification 属性,而名称暗示该元素可能是重复的。将在答案中详细说明。

以上是关于WCF 从 XML 接收空模型的主要内容,如果未能解决你的问题,请参考以下文章

类型化数据集 + WCF。服务正在接收空数据表

WCF服务接收空请求

WCF 客户端,XML 命名空间前缀导致空对象

无法在 wcf c# 中接收 xml 发布请求值

从 ASP Net Web API POST 请求中删除空的 xml 标记

WCF从角度发布Json