【问题标题】:WCF web service Data Members defaulting to nullWCF Web 服务数据成员默认为空
【发布时间】:2010-04-29 13:14:19
【问题描述】:

我是 WCF 的新手,我创建了一个简单的 REST 服务来接受一个订单对象(来自 XML 文件的一系列字符串),将该数据插入数据库,然后返回一个包含结果的订单对象。为了测试该服务,我创建了一个小型 Web 项目并通过从 xml 文档创建的流发送。

问题在于,即使 xml 文档中的所有项目都放入了流中,服务在接收数据时也会取消其中的一些。例如 lineItemId 将有一个值,但发货状态将显示为 null。我逐步完成 xml 创建并验证是否正在发送所有值。但是,如果我清除数据成员并更改名称,它可以工作。任何帮助将不胜感激。

这是接口代码

 [ServiceContract(Namespace="http://companyname.com/wms/")]
public interface IShipping
{

    [OperationContract]
    [WebInvoke(Method = "POST", UriTemplate = "/Orders/UpdateOrderStatus/", BodyStyle=WebMessageBodyStyle.Bare)]
    ReturnOrder UpdateOrderStatus(Order order);
}


[DataContract(Namespace="http://companyname.com/wms/order")]
public class Order
{
    [DataMember]
    public string lineItemId { get; set; }

    [DataMember]
    public string shipmentStatus { get; set; }

    [DataMember]
    public string trackingNumber { get; set; }

    [DataMember]
    public string shipmentDate { get; set; }

    [DataMember]
    public string delvryMethod { get; set; }

    [DataMember]
    public string shipmentCarrier { get; set; }
}

[DataContract]
public class ReturnOrder
{
    [DataMember(Name = "Result")]
    public string Result { get; set; }

}

这是我用来发送 Order 对象的内容:

string lineId = txtLineItem.Text.Trim();
    string status = txtDeliveryStatus.Text.Trim();
    string TrackingNumber = "1x22-z4r32";
    string theMethod = "Ground";
    string carrier = "UPS";
    string ShipmentDate = "04/27/2010";

    XNamespace nsOrders = "http://tempuri.org/order";
    XElement myDoc =
        new XElement(nsOrders + "Order",
            new XElement(nsOrders + "lineItemId", lineId),
            new XElement(nsOrders + "shipmentStatus", status),
            new XElement(nsOrders + "trackingNumber", TrackingNumber),
            new XElement(nsOrders + "delvryMethod", theMethod),
            new XElement(nsOrders + "shipmentCarrier", carrier),
            new XElement(nsOrders + "shipmentDate", ShipmentDate)
    );

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost:3587/Deposco.svc/wms/Orders/UpdateOrderStatus/");
    request.Method = "POST";
    request.ContentType = "application/xml";

    try
    {
        request.ContentLength = myDoc.ToString().Length;
        StreamWriter sw = new StreamWriter(request.GetRequestStream());
        sw.Write(myDoc);
        sw.Close();

        using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
        {

            StreamReader reader = new StreamReader(response.GetResponseStream());
            string responseString = reader.ReadToEnd();

            XDocument.Parse(responseString).Save(@"c:\DeposcoSvcWCF.xml");
        }

    }
    catch (WebException wEx)
    {
        Stream errorStream = ((HttpWebResponse)wEx.Response).GetResponseStream();
        string errorMsg = new StreamReader(errorStream).ReadToEnd();
    }

来自 Web.Config 的绑定

<system.serviceModel>
    <services>
        <service behaviorConfiguration="DesposcoService.ShippingServiceBehavior" name="DesposcoService.ShippingService">
            <endpoint address="wms" binding="webHttpBinding" contract="DesposcoService.IShipping" behaviorConfiguration="REST" bindingNamespace="http://companyname.com/wms">
                <identity>
                    <dns value="localhost"/>
                </identity>
            </endpoint>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        </service>
    </services>
    <behaviors>
        <serviceBehaviors>
            <behavior name="DesposcoService.ShippingServiceBehavior">
                <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
                <serviceMetadata httpGetEnabled="true"/>
                <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
                <serviceDebug includeExceptionDetailInFaults="true"/>
            </behavior>
        </serviceBehaviors>
        <endpointBehaviors>
            <behavior name="REST">
                <webHttp/>
            </behavior>
        </endpointBehaviors>
    </behaviors>
</system.serviceModel>

【问题讨论】:

    标签: asp.net wcf rest datamember


    【解决方案1】:

    我发现了这一点(显然与詹姆斯差不多同时)。

    问题在于DataContractSerializer,这里有一个重现它的测试用例:

    class Program
    {
        static void Main(string[] args)
        {
            XNamespace ns = "http://tempuri.org/";
            XElement element =
                new XElement(ns + "MyRequest",
                    new XElement(ns + "ID", 5),
                    new XElement(ns + "Name", "Test"),
                    new XElement(ns + "Description", "This is a test"));
    
            DataContractSerializer serializer = new
                DataContractSerializer(typeof(MyRequest));
            using (XmlReader reader = element.CreateReader())
            {
                MyRequest request = (MyRequest)serializer.ReadObject(reader);
                Console.WriteLine("ID: {0}, Name: {1}, Description: {2}",
                    request.ID, request.Name, request.Description);
            }
            Console.ReadLine();
        }
    
        [DataContract(Name = "MyRequest", Namespace = "http://tempuri.org/")]
        public class MyRequest
        {
            [DataMember]
            public int ID { get; set; }
    
            [DataMember]
            public string Name { get; set; }
    
            [DataMember]
            public string Description { get; set; }
        }
    }
    

    如果您运行它,您会看到 Description 属性为空。

    发生这种情况是因为DataContractSerializer 期望成员按字母顺序排列。当您对客户端和服务都使用DataContractSerializer 时,这工作得很好......当您手动生成 XML 时,这不是很好。

    如果将Order 属性添加到DataMember 属性,它会起作用:

        [DataContract(Name = "MyRequest", Namespace = "http://tempuri.org/")]
        public class MyRequest
        {
            [DataMember(Order = 0)]
            public int ID { get; set; }
    
            [DataMember(Order = 1)]
            public string Name { get; set; }
    
            [DataMember(Order = 2)]
            public string Description { get; set; }
        }
    

    这次它找到了Description 和所有其他字段。

    因此,要解决此问题,您可以执行以下任一操作:

    • Order 参数添加到DataMember 属性以匹配您实际计划生成XML 的顺序;或

    • 确保在客户端按字母顺序(按元素名称)添加元素。

    我不是特别喜欢这两种解决方法。它们看起来很老套,很容易破解。我认为对于 POX 服务,我更喜欢使用 XmlSerializer 而不是 DataContractSerializer,因为这样的事情不那么挑剔,但它似乎并不完全适用于 webHttpBinding。有更多时间时值得研究的东西。

    【讨论】:

    • 我确实为服务合同和数据合同定义了命名空间。他们里面有公司信息,所以我删除了它们以进行发布。但是,在将它们全部删除之后,我每次尝试提交给它的时候都会从服务器收到错误的请求 400。
    • @James:您能否在问题中包含该信息(必要时匿名)?还要发布您正在使用的绑定/配置 - 如果没有完整的可重现案例,很难诊断。
    • 当然,一旦我知道去哪里编辑问题,我会添加它(我是堆栈溢出的新手)
    • 好的,我已经更新了它,但是用公司名称替换​​了公司名称。感谢您迄今为止提供的信息,如果我可以添加任何内容,请告诉我。
    • @Rob:一旦你知道要搜索什么(按字母顺序),这似乎是一种众所周知的行为。这必须是设计使然;序列化程序不会“意外”按字母顺序重新排序所有字段。 为什么他们以这种方式设计它更像是一个谜......
    猜你喜欢
    • 2011-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-25
    • 2010-11-30
    • 2011-08-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多