无法跨WCF传输标准可序列化对象

时间:2013-10-10 15:49:00

标签: wcf

我创建了一个非常简单的服务器和客户端控制台应用程序,演示了我遇到的问题,我试图将可序列化对象的实例传递给客户端,但它在服务器上失败。

我错过了什么?我并不担心现在使用DataContracts进行面向服务 - 我只是想了解为什么现在的代码不会将EJob带到客户端(但它会调用'来自服务器的消息'消息)< / p>

非常感谢。

修改

即使我使用DataContract属性(如下所示)装饰EJob类,它仍然无效 - 我在客户端收到的对象将LastName设置为null ?????

[DataContract]
public class EJob
{
    [DataMember]
    public string LastName = "Smith";
}

服务器

namespace testServer
{
    [ServiceContract()]
    public interface IRemoteClient
    {
        [OperationContract]
        void SayHi(string msg);

        [OperationContract]
        void ProcessJob(EJob job);
    }

    [Serializable()]
    public class EJob
    {
        public string LastName = "Smith";
    }

    class Program
    {
        static void Main(string[] args)
        {
            MngrServer.SendJob();
        }
    }

    public class MngrServer
    {
        public static void SendJob()
        {
            try
            {
                // send this off to the correct exe
                NetTcpBinding binding = new NetTcpBinding(SecurityMode.None, true);

                string address = string.Format("net.tcp://localhost:33888/BatchMananger/client");
                EndpointAddress epa = new EndpointAddress(address);

                // create the proxy pointing to the correct exe
                IRemoteClient clientProxy = ChannelFactory<IRemoteClient>.CreateChannel(binding, epa);

                clientProxy.SayHi("Hello from server");  <-- THIS WORKS FINE

                EJob job = new EJob { LastName = "Janssen" };

                clientProxy.ProcessJob(job);             <-- THIS RAISES AN EXCEPTION see below...
            }
            catch (Exception ex)
            {
                string msg = ex.Message;

                //The formatter threw an exception while trying to deserialize the message: There was an error while 
                //trying to deserialize parameter http://tempuri.org/:job. The InnerException message was ''EndElement' 'job' 
                //from namespace 'http://tempuri.org/' is not expected. Expecting element 'LastName'.'.  
            }

        }
    }

}

客户端

namespace testClient
{
    [ServiceContract()]
    public interface IRemoteClient
    {
        [OperationContract]
        void SayHi(string msg);

        [OperationContract]
        void ProcessJob(EJob job);
    }

    [Serializable()]
    public class EJob 
    {
        public string LastName = "Smith";
    }

    class Program
    {
        static void Main(string[] args)
        {
            MngrClient.Prepare();
            Console.ReadLine();
        }
    }

    /// <summary>
    /// STATIC / INSTANCE 
    /// </summary>
    public class MngrClient : IRemoteClient
    {
        public void SayHi(string msg)
        {
            Console.WriteLine(msg);
        }

        public void ProcessJob(EJob job)
        {
            Console.WriteLine(job.LastName);
        }

        public static void Prepare()
        {
            // allow this class to be used! - so instances are created and info directly passed on to its static members.
            ServiceHost sh = new ServiceHost(typeof(MngrClient));

            // create the net binding
            NetTcpBinding binding = new NetTcpBinding(SecurityMode.None, true);

            // define the tcpaddress
            string address = string.Format("net.tcp://localhost:33888/BatchMananger/client");

            // add a service point so my server can reach me
            sh.AddServiceEndpoint(typeof(IRemoteClient), binding, address);

            // now open the service for business
            sh.Open();
        }

    }

}

2 个答案:

答案 0 :(得分:2)

您的EJob datacontract位于服务器与客户端的不同命名空间中。您需要在同一名称空间中声明这两个类,或使用属性在客户端上设置名称空间以匹配服务器上的名称空间

(Datacontract属性有一个你可以传递的命名空间值,或者你可以使用一个单独的命名空间属性来告诉WCF使用备用命名空间用于合同,不记得我的头顶了)

修改 刚验证 - 它是您想要的DataContractAttribute的Namespace属性,因此在您的客户端声明中:

[DataContract(Namespace="EJobNamespaceAsItIsDeclaredOnTheServer")]
public class EJob ...

现在,将所有DataContracts放在客户端和服务器都引用的单独程序集(称为契约程序集)中是很常见的。您只需要该程序集中的合同类定义,不需要其他任何内容。

答案 1 :(得分:0)

你以某种方式让它有点倒退......

  • 根据IRemoteClient的服务合同,您应该在服务器端有一个实现该接口的实现类

    public class ServiceImplementation : IRemoteClient
    {
       public void SayHi(string msg)
       { 
           .....
       }
    
       public void ProcessJob(EJob job)
       {
           .....
       }
    }
    

    另外:服务方法应该返回某些东西给调用者!没有返回类型,你有点创建一个服务的黑洞 - 你可以调用它的方法,但没有任何东西被返回....另外:服务实现类应 < em> NOT 自己托管!将其作为一个单独的类

  • 您应该在托管此服务的服务器端拥有主机类

    public class HostForYourService
    {
        public HostForYourService()
        {
            // send this off to the correct exe
            NetTcpBinding binding = new NetTcpBinding(SecurityMode.None, true);
    
            string address = string.Format("net.tcp://localhost:33888/BatchMananger/client");
            EndpointAddress epa = new EndpointAddress(address);
    
            ServiceHost sh = new ServiceHost(typeof(ServiceImplementation));
    
            // define the tcpaddress
            sh.AddServiceEndpoint(typeof(IRemoteClient), binding, address);
    
            // now open the service for business
            sh.Open();
        }
    
  • 然后您的客户端应为此服务构建客户端代理并将其命名为

    public class YourServiceClient
    {
       public void CallService() 
       {
            NetTcpBinding binding = new NetTcpBinding(SecurityMode.None, true);
    
            string address = string.Format("net.tcp://servername:33888/BatchMananger/client");
            EndpointAddress epa = new EndpointAddress(address);
    
            // create the proxy pointing to the correct exe
            IRemoteClient clientProxy = ChannelFactory<IRemoteClient>.CreateChannel(binding, epa);
    
            clientProxy.SayHi("Hello from server");  <-- THIS WORKS FINE
    
            EJob job = new EJob { LastName = "Janssen" };
    
            clientProxy.ProcessJob(job);
        }
    }
    

但同样:通常,您的服务方法应返回客户端可以操作的内容 - 毕竟,您通常不希望在服务器上执行Console.WriteLine - 你想要计算某些东西,查找某些东西等,并将响应返回给客户,然后依次可以将结果输出到控制台或其他东西......