【问题标题】:Is my objective possible using WCF (and is it the right way to do things?)使用 WCF 是否可以实现我的目标(这是正确的做事方式吗?)
【发布时间】:2010-04-05 23:14:51
【问题描述】:

我正在编写一些软件来修改 Windows Server 的配置(例如 MS-DNS、IIS、文件系统的一部分)。我的设计有一个构建服务器配置状态的内存对象图的服务器进程和一个请求该对象图的客户端。然后,服务器将序列化该图,将其发送到客户端(可能使用 WCF),然后服务器对该图进行更改并将其发送回服务器。服务器接收到图形并继续对服务器进行修改。

但是,我了解到 WCF 中的对象图序列化并不像我最初想象的那么简单。我的对象具有层次结构,并且许多对象具有参数化构造函数和不可变的属性/字段。还有许多集合、数组和字典。

我对 WCF 序列化的理解是它需要使用 XmlSerializer 或 DataContractSerializer,但 DCS 对我的对象图的设计施加了限制(不可变数据似乎是正确的,它还需要无参数的构造函数)。我了解 XmlSerializer 允许我使用我自己的类,只要它们实现 ISerializable 并具有反序列化器构造函数。这对我来说很好。

我和我的一个朋友谈过这件事,他主张采用仅数据传输对象的路线,我必须维护一个单独的 DataContract 对象图来传输数据并重新实现我的服务器客户端上的对象。

我的另一个朋友说,因为我的服务只有两个操作(“GetServerConfiguration”和“PutServerConfiguration”),所以完全跳过 WCF 并实现我自己的使用 Sockets 的服务器可能是值得的。

所以我的问题是:

  • 以前有没有人遇到过类似的问题,如果有,有更好的方法吗?将整个对象图发送到客户端进行处理是否明智?我是否应该将其分解,以便客户端根据需要请求对象图的一部分并仅发送已更改的位(从而减少与并发相关的风险?)?
  • 如果向下发送对象图是正确的方法,那么 WCF 是正确的工具吗?
  • 如果 WCF 是正确的,那么让 WCF 序列化我的对象图的最佳方法是什么?

【问题讨论】:

  • 当您说图时,您指的是无向图,其中多个节点可以链接到许多其他节点?如果是这样,WCF 会为对象分配正确的值。因此,您的一个节点最终可能会得到与预期不同的值.....如果这就是您所说的图表。
  • 嗯,例如,发送的对象之一是我写的一个代表DNS区域的类,这个类包含一个DNS资源记录列表,并且这些RR对象引用了它们的父DNS区。所以它是一个循环有向图。
  • 实际上恰恰相反——XmlSerializer 需要一个无参数的构造函数,而 DataContractSerializer 不需要

标签: c# wcf serialization


【解决方案1】:

对象图可以与 DataContract 序列化一起使用。

注意:确保您保留了对象引用,这样您就不会在图表中出现同一对象的多个副本,而这些副本都应该是相同的引用,默认情况下行为不会像这样保留身份。

这可以通过在构造DataContractSerializer 时指定preserveObjectReferences 参数或通过为DataContractAttribute 上的IsReference 属性指定true 来完成(最后一个属性需要.NET 3.5SP1强>)。

但是,在通过 WCF 发送对象图时,如果您不注意确保图保持在合理的大小,则可能会违反 WCF 配额(并且有很多配额)。

对于net.tcp 传输,例如,要设置的重要的是maxReceivedMessageSizemaxStringContentLengthmaxArrayLength。更不用说图表中允许的 65335 个不同对象的隐藏配额 (maxObjectsInGraph),这很难被覆盖。

您还可以使用仅通过DataContractSerializer 公开读取访问器且没有无参数构造函数的类:

using System;
using System.IO;
using System.Runtime.Serialization;

class DataContractTest
{
    static void Main(string[] args)
    {
      var serializer = new DataContractSerializer(typeof(NoParameterLessConstructor));

      var obj1 = new NoParameterLessConstructor("Name", 1);

      var ms = new MemoryStream();
      serializer.WriteObject(ms, obj1);

      ms.Seek(0, SeekOrigin.Begin);

      var obj2 = (NoParameterLessConstructor)serializer.ReadObject(ms);

      Console.WriteLine("obj2.Name: {0}", obj2.Name);
      Console.WriteLine("obj2.Version: {0}", obj2.Version);
    }

    [DataContract]
    class NoParameterLessConstructor
    {
        public NoParameterLessConstructor(string name, int version)
        {
          Name = name;
          Version = version;
        }

        [DataMember]
        public string Name { get; private set; }
        [DataMember]
        public int Version { get; private set; }
    }
}

这是因为DataContractSerializer 可以在不调用构造函数的情况下实例化类型。

【讨论】:

  • DataContracts 的特点是它们更适合 DTO 对象而不是更复杂的 POCO,例如,您必须有无参数构造函数,并且每个字段都必须是可变的,但我现有的设计具有参数化构造函数和不可变的字段。
  • 实际上,DataContractSerializer 不需要无参数构造函数,并且它还可以使用私有 setter 序列化属性,例如,因此您可以限制域对象公开的内容(如果这符合您对不可变的定义)。我已更新我的答案以包含示例代码。
  • 请注意,您必须使用 DataContract 和 DataMember 属性注释您的类型才能工作,如果您无法将类型更改为具有这些属性,那么是的,您提到的限制确实如此申请。
【解决方案2】:

你把自己和序列化器搞混了:

  • XmlSerializer 需要无参数构造函数,因为在反序列化时,.NET 运行时将实例化该类型的新对象,然后设置其属性

  • DataContractSerializer 没有这样的要求

查看blog post by Dan Rigsby,它解释了序列化程序的所有荣耀并比较了两者。

现在对于您的设计 - 我的主要问题是:有一个返回所有设置的函数是否有意义,客户端操作这些设置,然后另一个函数接收所有信息?

你不能把这些东西分解成更小的块,更小的方法调用吗?例如。有单独的服务方法来设置配置的每个单独项目?这样你就可以

  • 减少通过网络发送的数据量 - 要序列化和反序列化的对象图会简单得多
  • 使您的配置服务更加精细 - 例如如果有人需要设置一个小属性,该客户端不需要读取整个大服务器配置,设置一个属性,然后发回大块 - 只需调用适当的方法来设置一个属性

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-11
    • 2019-09-01
    • 1970-01-01
    • 2015-02-15
    • 1970-01-01
    相关资源
    最近更新 更多