views:

857

answers:

1

I have a simple WCF service that returns the time from the server. I've confirmed that data is being sent by checking with Fiddler. Here's the result object xml that my service sends.

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

So, as far as I can tell, there's nothing wrong on the server end. It's receiving requests and returning results.

But on my silverlight client, all the members of the returned object are either null, blank or a default vaule. As you can see the server returns the current date and time. But in silverlight, theTime property on my object is set to 1/1/0001 12:00 AM (default value).

Sooo methinks that the DataContracts do not match up between the server and the silverlight client. Here's the DataContract for the server

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

Incredibly simple. And here's the datacontract on my silverlight client.

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

Literally the only difference is the namespaces within the application. But still the values being returned are null, empty or a .NET default.

Thanks for you help!

UPDATE

Here is the ClientBase that all my services run through. I read an excellent article here to construct it.

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

Here is my interface

 [ServiceContract]      

    public interface ITestService
            {

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

                Time EndGetTime( IAsyncResult result );

            }

Now I have my service class that makes calls through my BaseService class using this 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 );
    }
}

Finally here is the code that actually calls everything and does the work.

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

            } );

Whew....I hope no one is lost from all this code I posted :P

A: 

Because you don't set the Namespace property on your DataContractAttribute, the namespace will be sythesized from the .NET class/namespace. You can see this in the SOAP message example you posted:

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

In order to have the contracts be considered equal, you must set the Namespace property on the DataContract to the same value on both sides. That might look a little something like this:

[DataContract(Namespace="urn:my-test-namespace")]
Drew Marsh
These seems very, very, very plausible that this is the problem. I'm going to try it the next free moment I get. Thanks.
Matt
Interesting, and because I had different Namespaces for each application, the DataContracts don't work. I added in some Namespaces and it all works perfect now! Thank you!!
Matt
Exactly. Glad I could help!
Drew Marsh