2010-04-29 6 views
1

Je suis nouveau à WCF et créé un service REST simple pour accepter un objet de commande (série de chaînes à partir du fichier XML), insérer ces données dans une base de données, puis renvoyer un objet de commande contient les résultats. Pour tester le service, j'ai créé un petit projet web et envoyé un flux créé à partir d'un doc xml.Membres du service Web WCF par défaut null

Le problème est que même si tous les éléments du doc ​​xml sont placés dans le flux, le service annule certains d'entre eux lorsqu'il reçoit les données. Par exemple, lineItemId aura une valeur mais l'état de l'envoi affichera null. Je parcours la création de xml et vérifie que toutes les valeurs sont envoyées. Cependant, si j'efface les datamembers et change les noms, cela peut fonctionner. Toute aide serait appréciée.

Ceci est le code d'interface

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

} 

C'est ce que j'utilise pour envoyer sur un objet de commande:

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

Manchettes de 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> 

Répondre

4

I compris cela (apparemment à la même époque que James).

La question est la DataContractSerializer, et voici un test qui reproduit:

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

Si vous exécutez, vous verrez qu'il arrive vide pour la propriété Description.

Cela se produit car le DataContractSerializer s'attend à ce que les membres soient dans l'ordre alphabétique. Cela fonctionne très bien lorsque vous utilisez le DataContractSerializer pour le client et le service ... pas très bien lorsque vous générez manuellement XML.

Si vous ajoutez Order propriétés aux attributs DataMember, cela fonctionne:

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

Cette fois, il trouve le Description et tous les autres domaines.

Donc, pour résoudre le problème, vous pouvez faire une des opérations suivantes:

  • Ajouter Order arguments à l'DataMember attributs pour correspondre à l'ordre dans lequel vous envisagez de générer réellement XML; ou

  • Assurez-vous d'ajouter des éléments dans l'ordre alphabétique (par nom d'élément) du côté client.

Je ne suis pas particulièrement friand de ces solutions de contournement. Ils semblent hackish et facile à casser. Je pense que pour les services POX, je préférerais utiliser le XmlSerializer au lieu du DataContractSerializer étant donné que c'est moins compliqué à propos de choses comme ça, mais ça ne semble pas fonctionner correctement avec webHttpBinding. Quelque chose mérite d'être étudié quand il y a plus de temps.

+0

J'ai effectivement des espaces de noms définis pour le contrat de service et le contrat de données. Ils avaient des informations sur la société, alors je les ai retirés pour les poster.Après les avoir tous supprimés, je reçois une mauvaise requête 400 du serveur chaque fois que j'essaie de la soumettre. – James

+0

@James: Pouvez-vous inclure cette information (anonymisée si nécessaire) dans la question? Publiez également les liaisons/configurations que vous utilisez - il est difficile de diagnostiquer sans un cas reproductible complet. – Aaronaught

+0

Bien sûr, une fois que je trouve où aller pour modifier la question, je vais l'ajouter (je suis nouveau à la pile de débordement) – James