WCF Web 服务数据成员默认为 null

发布于 2024-08-30 22:41:02 字数 4503 浏览 8 评论 0原文

我是 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>

I am new to WCF and created a simple REST service to accept an order object (series of strings from XML file), insert that data into a database, and then return an order object that contains the results. To test the service I created a small web project and send over a stream created from an xml doc.

The problem is that even though all of the items in the xml doc get placed into the stream, the service is nullifying some of them when it receives the data. For example lineItemId will have a value but shipment status will show null. I step through the xml creation and verify that all the values are being sent. However, if I clear the datamembers and change the names around, it can work. Any help would be appreciated.

This is the interface code

 [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; }

}

This is what I'm using to send over an Order object:

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();
    }

Bindings from 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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

去了角落 2024-09-06 22:41:02

我想通了这一点(显然詹姆斯几乎在同一时间想通了)。

问题出在 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 的框。当有更多时间时,值得研究的东西。

I figured this out (apparently at around the same time James did).

The issue is with the DataContractSerializer, and here is a test case that reproduces it:

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; }
    }
}

If you run this, you'll see that it comes up empty for the Description property.

This happens because the DataContractSerializer expects members to be in alphabetical order. This works fine when you are using the DataContractSerializer for both the client and service... not so great when you're manually generating XML.

If you add Order properties to the DataMember attributes, it works:

    [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; }
    }

This time it finds the Description and all other fields.

So to resolve the issue, you can do either of the following:

  • Add Order arguments to the DataMember attributes to match the order in which you actually plan to generate XML; or

  • Make sure you add elements in alphabetical order (by element name) on the client side.

I'm not particularly fond of either of these workarounds. They seem hackish and easy to break. I think for POX services I'd prefer to use the XmlSerializer instead of the DataContractSerializer since it's less finicky about things like that, but it doesn't quite seem to work out of the box with webHttpBinding. Something worth investigating when there's more time.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文