客户端 WCF DataContract 具有来自服务的空/null 值

发布于 2024-08-26 21:21:08 字数 5477 浏览 11 评论 0原文

我有一个简单的 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 客户端上,返回对象的所有成员要么为 null,要么为空白,要么为默认值。如您所见,服务器返回当前日期和时间。但在 silverlight 中,我的对象上的 Time 属性设置为 1/1/0001 12:00 AM(默认值)。

Sooo 我认为服务器和 silverlight 客户端之间的数据契约不匹配。这是服务器的 DataContract

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

非常简单。这是我的 silverlight 客户端上的数据合同。

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

从字面上看,唯一的区别是应用程序内的名称空间。但返回的值仍然为 null、空或 .NET 默认值。

谢谢你的帮助!

更新

这是我所有服务运行的 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

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

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

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

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

发布评论

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

评论(2

悲凉≈ 2024-09-02 21:21:08

因为您没有设置 DataContractAttribute 上的命名空间属性,命名空间将从 .NET 类/命名空间合成。您可以在您发布的 SOAP 消息示例中看到这一点:

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

为了使契约被视为相等,您必须将 DataContract 上的 Namespace 属性设置为双方相同的值。这可能看起来有点像这样:

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

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")]
回心转意 2024-09-02 21:21:08

扩展 Drew Marsh 的正确答案(+1 - thx)我生成了一个正在运行的服务引用,但是当我尝试使用 Wcf 客户端工厂实现正确的接口(但命名空间不同)时,我遇到了问题描述的。

我没有简单的方法来确定“正确”的命名空间应该是什么,但只需将以下属性从服务引用的 DataContract 实体复制到 Wcf 客户端工厂实现中的属性即可解决该问题;

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

Extending on Drew Marsh's correct answer (+1 - thx) I had a generated Service Reference which was working, but when I tried to use the Wcf Client Factory one implementing the correct interface (but the namespace was different) then I was experiencing the problem described.

I had no easy way to work out what the "correct" namespace should have been but simply copying the following attributes from the service reference's DataContract entity to the one in the Wcf Client Factory implementation solved the issue;

[System.Runtime.Serialization.DataContractAttribute(Name = "BOSPrice", Namespace = "http://schemas.datacontract.org/2004/07/BOSDataService")]
  [System.SerializableAttribute()]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文