Клиентский WCF DataContract имеет пустые / нулевые значения из сервиса - PullRequest
9 голосов
/ 26 марта 2010

У меня есть простой сервис WCF, который возвращает время с сервера. Я подтвердил, что данные отправляются путем проверки с помощью Fiddler. Вот объект результата xml, который отправляет мой сервис.

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

Итак, насколько я могу судить, на стороне сервера нет ничего плохого. Он получает запросы и возвращает результаты.

Но в моем клиенте silverlight все члены возвращаемого объекта либо нулевые, либо пустые, либо vaule по умолчанию. Как видите сервер возвращает текущую дату и время. Но в Silverlight свойство theTime моего объекта установлено на 01.01.01 00:00 (значение по умолчанию).

Sooo отмечает, что DataContracts не совпадают между сервером и клиентом Silverlight. Вот DataContract для сервера

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

Невероятно просто. А вот и данные по моему клиенту Silverlight.

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

Буквально единственным отличием является пространство имен в приложении. Но все же возвращаемые значения являются нулевыми, пустыми или .NET по умолчанию.

Спасибо за помощь!

UPDATE

Вот ClientBase, через который работают все мои сервисы. Я прочитал отличную статью здесь , чтобы построить ее.

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

Вот мой интерфейс

 [ServiceContract]      

    public interface ITestService
            {

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

                Time EndGetTime( IAsyncResult result );

            }

Теперь у меня есть класс обслуживания, который выполняет вызовы через мой класс BaseService, используя этот интерфейс.

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

Наконец, вот код, который фактически вызывает все и выполняет работу.

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

            } );

Вот так ... Надеюсь, никто не потерял весь этот код, который я написал: P

Ответы [ 2 ]

19 голосов
/ 26 марта 2010

Поскольку вы не установили свойство пространства имен в своем DataContractAttribute, пространство имен будет синтезировано из класса / пространства имен .NET. Вы можете увидеть это в опубликованном вами примере сообщения SOAP:

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

Чтобы контракты считались равными, необходимо установить для свойства Namespace в DataContract одинаковое значение с обеих сторон. Это может выглядеть примерно так:

[DataContract(Namespace="urn:my-test-namespace")]
0 голосов
/ 30 сентября 2013

Расширение на правильный ответ Дрю Марша (+1 - thx) У меня была сгенерированная ссылка на службу, которая работала, но когда я попытался использовать Wcf Client Factory, реализовав правильный интерфейс (но пространство имен было другим), тогда я испытываю описанную проблему.

У меня не было простого способа выяснить, каким должно быть «правильное» пространство имен, а просто скопировать следующие атрибуты из объекта DataContract ссылки на сервис в объект в реализации Wcf Client Factory, чтобы решить проблему;

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