【问题标题】:How to implement an inherited Dictionary over WCF如何通过 WCF 实现继承的字典
【发布时间】:2008-11-05 23:17:30
【问题描述】:

我正在尝试实现一个与 WCF 一起使用的字典。我的要求是:

  • 实际(私有变量或基 class) 类型等价于 字典
  • 比较器 = System.StringComparer.InvariantCultureIgnoreCase
  • 自定义(覆盖/新建)添加(键, 值)方法(包括 验证)。
  • 覆盖 ToString()
  • 在客户端和主机上使用相同的类型

我尝试在 WCF 宿主项目和客户端项目共享的公共项目中使用此类:

[Serializable]
public class MyDictionary : Dictionary<string, object>
{
  public MyDictionary()
    : base(System.StringComparer.InvariantCultureIgnoreCase)
  { }

  public new void Add(string key, object value)
  { /* blah */ }

  public override string ToString()
  { /* blah */ }
}

[DataContract]
[KnownType(typeof(MyDictionary))]
[KnownType(typeof(object[]))]
[KnownType(typeof(double[]))]
[KnownType(typeof(string[]))]
[KnownType(typeof(DateTime[]))]
public class ResultClass
{
  public object Value{ get; set; }
  /* More properties */
}
public class ParmData
{
  public object Value{ get; set; }
  /* More properties */
}
[DataContract]
[KnownType(typeof(MyDictionary))]
[KnownType(typeof(object[]))]
[KnownType(typeof(double[]))]
[KnownType(typeof(string[]))]
[KnownType(typeof(DateTime[]))]
public class ParameterClass
{
  public List<ParmData> Data{ get; set; }
  /* More properties */
}

[OperationContract]
ResultClass DoSomething(ParameterClass args);

结果:

  • 当我将 MyDictionary 作为 ParameterClass.Data.Value 元素之一传递时,我得到一个缺失的 KnownType 异常。
  • 我可以安全地在 ResultClass 中返回 MyDictionary,但它不再是我的类型。它只是一个字典,不能转换为MyDictionary。还有 comparer = System.Collections.Generic.GenericEqualityComparer&lt;string&gt;,不是我要找的不区分大小写的比较器。

我寻求的帮助是解决我失败的尝试,或者以一种完全不同的方式来实现我所陈述的要求。任何解决方案都不应涉及将一个字典复制到另一个字典。

谢谢

【问题讨论】:

  • NetDataContractAttribute 是一个可行的选择吗?也就是说,您是否能够保证在客户端和服务器端都可以使用具有 MyDictionary 类的相同程序集的副本?
  • 感谢您回复我的帖子,但您尚未回答:我发布的代码是否适用于您的设置?还是抛出异常(或不带回数据)?
  • Marc - 我只对你的测试做了 1 次尝试。 clone.Data 抛出空值异常。感谢您的帮助,但 jezell 为我指明了正确的大方向。

标签: c# wcf c#-3.0


【解决方案1】:

将 CollectionDataContract 添加到 Dictionary 类中:

有关使用收集数据合约实现字典的更多信息,请查看此链接:

http://msdn.microsoft.com/en-us/library/aa347850.aspx

【讨论】:

  • 这看起来是正确的方法。唯一的问题是我最终在子命名空间中得到了一个新的 MyDictionary 类。我正在研究如何说服它使用正确的命名空间。 (social.msdn.microsoft.com/forums/en-US/wcf/thread/…)
  • 即使没有属性,序列化程序也支持基于字典的集合 - 这只是自定义架构(名称等)
【解决方案2】:

序言:请注意,添加“新”Add 不会阻止人们通过强制转换调用旧的Add。此外,就“mex”而言,这是一个非常模糊的数据合同——它需要如此开放吗? (很多 object 等等...)

首先:你没有错过一些[DataContract]/[DataMember] 标记吗?特别是:

  • ResultClass.Value
  • 参数数据
  • ParamData.Value
  • ParameterClass.Data

您能否准确地说明您使用的是哪个版本的 .NET? DataContractSerializer 等已通过服务包进行了调整。安装 3.5 SP1(我唯一需要做的事情)后,它至少会通过 DataContractSerializer(无 WCF 堆栈)进行序列化和反序列化,并调用正确的 Add 方法。

您能否检查以下内容是否适用于您的本地版本? (它适用于 3.5 SP1 和缺少的属性)[输出优先]:

1
MyDictionary
abc=123
def=ghi

代码:

        // or long-hand in C# 2.0
        ParameterClass pc = new ParameterClass {
            Data = new List<ParmData> { new ParmData {
                Value = new MyDictionary  {
                    {"abc",123},
                    {"def","ghi"}
                }}}};
        DataContractSerializer dcs = new DataContractSerializer(pc.GetType());
        string xml;
        using(StringWriter sw = new StringWriter())
        using(XmlWriter xw = XmlWriter.Create(sw)) {
            dcs.WriteObject(xw, pc);
            xw.Close();
            xml = sw.ToString();
        }
        using(StringReader sr = new StringReader(xml)) {
            ParameterClass clone = (ParameterClass)dcs.ReadObject(XmlReader.Create(sr));
            Console.WriteLine(clone.Data.Count);
            Console.WriteLine(clone.Data[0].Value.GetType().Name);
            MyDictionary d = (MyDictionary)clone.Data[0].Value;
            foreach (KeyValuePair<string, object> pair in d)
            {
                Console.WriteLine("{0}={1}", pair.Key, pair.Value);
            }
        }

显然这只是测试DataContractSerializer(没有整个 WCF 堆栈),但它似乎可以工作......所以:相同的代码是否适用于您的本地版本的 .NET?如果没有,是否可以选择安装最新的 3.0 服务包? (最好通过安装 3.5 SP1)。

有关信息,我得到 xml:

<?xml version="1.0" encoding="utf-16"?><ParameterClass xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/"><Data><ParmData><Value xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" i:type="d4p1:ArrayOfKeyValueOfstringanyType"><d4p1:KeyValueOfstringanyType><d4p1:Key>abc</d4p1:Key><d4p1:Value xmlns:d6p1="http://www.w3.org/2001/XMLSchema" i:type="d6p1:int">123</d4p1:Value></d4p1:KeyValueOfstringanyType><d4p1:KeyValueOfstringanyType><d4p1:Key>def</d4p1:Key><d4p1:Value xmlns:d6p1="http://www.w3.org/2001/XMLSchema" i:type="d6p1:string">ghi</d4p1:Value></d4p1:KeyValueOfstringanyType></Value></ParmData></Data></ParameterClass>

【讨论】:

  • 已安装的 .NET 运行时:1.0.3705.0 版本 1.0 RTM; 1.1.4322.2407 版本 1.1 Post-SP1,带有 KB928366; 2.0.50727.1433; 3.0.4506.648。是的,合同必须是开放式的。这是遗留系统的包装器。
  • 嗯...看起来像 3.0SP1
【解决方案3】:
  • 按照 jezell 的建议使用 CollectionDataContract 属性
  • 使用 /collectionType 参数,使用 SvcUtil 手动生成参考(代理)代码。 vs2008 服务参考 GUI 不支持此参数。

来源:WCF Collection Type Sharing

【讨论】:

  • 如果解决方案是由其他用户提供的,您为什么要接受自己的答案?
【解决方案4】:

我终于找到了一种方法来做到这一点。我发现this link 指出了正确的方向,但我会尝试总结一下。

  1. 确保将[CollectionDataContract] 添加到您的自定义集合中
  2. 像往常一样通过 VS 添加服务引用
  3. 展开服务引用并查找 Reference.svcmap 文件
  4. 在客户端选项节点下,您将看到

将其替换为以下 xml。



  1. 右键单击服务参考并单击更新。它不应该再生成那个“代理”类,而是让你使用你的共享类。

【讨论】:

  • 编辑自动生成的文件通常不是一个好主意。这很容易意外覆盖您的更改。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-13
  • 2015-02-23
  • 2019-03-21
  • 2011-10-07
  • 1970-01-01
  • 2023-03-14
相关资源
最近更新 更多