2010-03-26 23 views
9

J'ai un service WCF simple qui renvoie l'heure du serveur. J'ai confirmé que les données sont envoyées en vérifiant avec Fiddler. Voici l'objet de résultat xml que mon service envoie.Le client WCF DataContract a des valeurs vides/nulles du service

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> 
    <s:Body> 
     <GetTimeResponse xmlns="http://tempuri.org/"> 
      <GetTimeResult xmlns:a="http://schemas.datacontract.org/2004/07/TestService.DataObjects" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> 
       <a:theTime>2010-03-26T09:14:38.066372-06:00</a:theTime> 
      </GetTimeResult> 
     </GetTimeResponse> 
    </s:Body> 
    </s:Envelope> 

Donc, autant que je sache, il n'y a rien de mal à la fin du serveur. Il reçoit des demandes et renvoie des résultats.

Mais sur mon client Silverlight, tous les membres de l'objet retourné sont soit null, vide ou une valeur par défaut. Comme vous pouvez le voir, le serveur renvoie la date et l'heure actuelles. Mais dans Silverlight, la propriété Time sur mon objet est définie sur 1/1/0001 12:00 AM (valeur par défaut).

Sooo suggère que les DataContracts ne correspondent pas entre le serveur et le client Silverlight. Voici le DataContract pour le serveur

[DataContract] 
public class Time 
{ 
    [DataMember] 
    public DateTime theTime { get; set; } 
} 

Incroyablement simple. Et voici le contrat de données sur mon client Silverlight. Littéralement, la seule différence réside dans les espaces de noms au sein de l'application. Mais toujours les valeurs retournées sont nulles, vides ou un .NET par défaut.

Merci pour votre aide!

MISE À JOUR

Voici le ClientBase que tous mes services traversent. J'ai lu un excellent article here pour le construire.

public class ClientBase<T> where T :class 
{ 
    private T Channel { get; set; } 

    private Type ContractType { get; set; } 

    private ClientBase() 
    { 
     ContractType = typeof(T); 
    } 

    public ClientBase(string endPointConfiguration) :this() 
    { 
     Channel = new ChannelFactory<T>(endPointConfiguration).CreateChannel(); 
    } 

    public ClientBase(EndpointAddress address, Binding binding):this() 
    { 
     Channel = new ChannelFactory<T>(binding, address).CreateChannel(); 
    } 

    public void Begin(string methodName, object state, params object[] parameterArray) 
    { 
     Begin(methodName, null, state, parameterArray); 
    } 

    public void Begin(string methodName, EventHandler<ClientEventArgs> callBack, object state, params object[] parameterArray) 
    { 
     if(parameterArray != null) 
     { 
      Array.Resize(ref parameterArray, parameterArray.Length + 2); 
     } 
     else 
     { 
      parameterArray = new object[2]; 
     } 

     parameterArray[ parameterArray.Length - 1 ] = new ObjectClientState {CallBack = callBack, MethodName = methodName, UserState = state}; 
     parameterArray[ parameterArray.Length - 2 ] = new AsyncCallback(OnCallBack); 
     ContractType.InvokeMember("Begin" + methodName, 
            System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.InvokeMethod | 
            System.Reflection.BindingFlags.Public, null, Channel, parameterArray); 

    } 

    private void OnCallBack(IAsyncResult result) 
    { 
     ObjectClientState state = result.AsyncState as ObjectClientState; 
     if(state == null) 
      return; 
     Object obj = ContractType.InvokeMember("End" + state.MethodName, 
               System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.InvokeMethod | 
               System.Reflection.BindingFlags.Public, null, Channel, new object[] {result}); 
     if(state.CallBack != null) 
     { 
      state.CallBack(this, new ClientEventArgs {Object = obj, UserState = state.UserState}); 
     } 
    } 

    public class ClientEventArgs : EventArgs 
    { 
     public object Object { get; set; } 
     public object UserState { get; set; } 

     public T LoadResult<T>() 
     { 
      if(Object is T) 
       return (T) Object; 
      return default(T); 
     } 
    } 

    private class ObjectClientState 
    { 
     public EventHandler<ClientEventArgs> CallBack { get; set; } 
     public string MethodName { get; set; } 
     public object UserState { get; set; } 
    } 
} 

Voici mon interface

[ServiceContract]  

    public interface ITestService 
      { 

       [OperationContract(AsyncPattern = true)] 
       IAsyncResult BeginGetTime(AsyncCallback callback, object state); 

       Time EndGetTime(IAsyncResult result); 

      } 

Maintenant, j'ai ma classe de service qui fait des appels dans ma classe BaseService utilisant cette interface.

public class TestSiteService : ClientBase<ITestService> 
{ 
    public TestSiteService (string endPointConfiguration):base(endPointConfiguration) { } 

    public TestSiteService (EndpointAddress address, Binding binding) : base(address, binding) { } 

    public void GetTime(EventHandler<ClientEventArgs> callBack) 
    { 
     Begin("GetTime", callBack, null, null); 
    } 
} 

Enfin, voici le code qui appelle réellement tout et fait le travail.

TestSiteService client = new TestSiteService (new EndpointAddress("http://localhost:3483/wcf/Service.svc"), new BasicHttpBinding()); 

client.GetTime(delegate(object res, ClientBase<ITestService>.ClientEventArgs e) 
      { 

       Dispatcher.BeginInvoke(() => lblDisplay.Text = "Welcome " + e.LoadResult<Time>().theTime); 

      }); 

Ouf .... J'espère que personne ne se perd de tout ce code que j'ai posté: P

+0

Pouvez-vous publier le code pour la méthode de service et le code client que vous utilisez? En outre, vous devez formater votre code correctement dans votre question. Modifiez la question, sélectionnez le code et appuyez sur Ctrl + K. –

+0

eek! Je ne sais pas comment cela est arrivé. Tout est fixé. Je vais poster mon client de service dans un peu – Matt

Répondre

19

Parce que vous ne définissez pas the Namespace property sur votre DataContractAttribute, l'espace de noms sera sythesized à partir du .NET classe/espace de noms. Vous pouvez le voir dans l'exemple de message SOAP que vous avez posté:

http://schemas.datacontract.org/2004/07/TestService.DataObjects

Pour que les contrats soient considérés comme égaux, vous devez définir la propriété Namespace sur le DataContract au même valeur des deux côtés.Cela pourrait paraître un peu quelque chose comme ceci:

[DataContract(Namespace="urn:my-test-namespace")] 
+0

Cela semble très, très, très plausible que c'est le problème. Je vais essayer le prochain moment de libre. Merci. – Matt

+1

Intéressant, et parce que j'avais des espaces de noms différents pour chaque application, les DataContracts ne fonctionnent pas. J'ai ajouté dans certains espaces de noms et tout fonctionne parfaitement maintenant! Je vous remercie!! – Matt

+0

Exactement. Heureux d'avoir pu aider! –

0

Extension de réponse de Drew Marsh (1 - thx) J'ai eu un service généré référence qui fonctionnait, mais quand j'ai essayé d'utiliser l'un client WCF usine mise en œuvre l'interface correcte (mais l'espace de noms était différent) alors je connaissais le problème décrit. Je n'avais aucun moyen facile de déterminer ce que l'espace de nommage "correct" aurait dû être mais copier simplement les attributs suivants de l'entité DataContract de la référence de service à celle de l'implémentation Wcf Client Factory a résolu le problème;

[System.Runtime.Serialization.DataContractAttribute(Name = "BOSPrice", Namespace = "http://schemas.datacontract.org/2004/07/BOSDataService")] 
    [System.SerializableAttribute()]