0

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

我错过了什么??我现在并不担心它是否使用 DataContracts 面向服务 - 我只是想了解为什么现在的代码不会将 EJob 带到客户端(但它确实调用了“来自服务器的 Hello”消息)

非常感谢。

编辑

即使我用 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();
        }

    }

}
4

2 回答 2

2

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

(Datacontract 属性有一个可以传递的命名空间值,或者有一个单独的命名空间属性可以用来告诉 WCF 为合同使用备用命名空间,我想不起来了)

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

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

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

于 2013-10-10T16:09:32.820 回答
0

不知怎的,你有点倒退了...

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

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

    另外:服务方法应该向调用者返回一些东西!没有返回类型,你有点创建服务的黑洞——你可以调用它的方法,但什么都没有返回……另外:服务实现类应该托管自己!使它成为一个单独的类

  • 然后,您应该在托管此服务的服务器端有一个主机类:

    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在服务器上执行 a - 您想要计算某些内容、查找某些内容等并返回 a对客户端的响应然后反过来可以例如将结果输出到控制台或其他东西....

于 2013-10-10T16:16:30.680 回答