How to implement an inherited Dictionary over WCF

梦想的初衷 提交于 2019-12-18 04:17:40

问题


I’m trying to implement a dictionary for use with WCF. My requirements are:

  • actual (private variable or base class) type equivalent to Dictionary
  • Comparer = System.StringComparer.InvariantCultureIgnoreCase
  • Custom (override/new) Add(key, value) method (to include validations).
  • Override ToString()
  • Use of the same type on both the client and host

I’ve attempted using this class in a common project shared by the WCF host and client projects:

[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);

Results:

  • When I pass MyDictionary as one of the ParameterClass.Data.Value elements, I get a missing KnownType exception.
  • I can safely return MyDictionary in the ResultClass, but it is no longer my type. It is just a Dictionary, and is not castable to MyDictionary. Also comparer = System.Collections.Generic.GenericEqualityComparer<string>, not the case insensitive comparer I’m looking for.

The help I’m asking for is to either fix my failed attempt, or a completely different way to achieve my stated requirements. Any solution should not involve copying one dictionary to another.

Thanks


回答1:


Add CollectionDataContract to the Dictionary class:

For more information on using collection data contracts to implement dictionaries, check this link:

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




回答2:


Preamble: note that adding a "new" Add doesn't stop people calling the old Add simply by casting. Also, in terms of "mex", that is a very vague data-contract - does it need to be so open-ended? (lots of object etc...)

First: haven't you missed a few [DataContract]/[DataMember] markers there? In particular:

  • ResultClass.Value
  • ParamData
  • ParamData.Value
  • ParameterClass.Data

Can you clarify exactly which version of .NET you are using? DataContractSerializer etc have been tweaked via service packs. With 3.5 SP1 installed (the only thing I have to hand) it does at least serialize and deserialize via DataContractSerializer (no WCF stack), and the correct Add method is called.

Can you check whether the following works with your local version? (it works for me with 3.5 SP1 and with the missing attributes) [output first]:

1
MyDictionary
abc=123
def=ghi

Code:

        // 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);
            }
        }

Obviously this just tests DataContractSerializer (without the entire WCF stack), but it seems to work... so: does the same code work with your local version of .NET? If not, is installing the latest 3.0 service pack an option? (ideally via installing 3.5 SP1).

For info, I get 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>



回答3:


  • Use the CollectionDataContract attribute as jezell suggested
  • Manually generate the reference (proxy) code with SvcUtil, using the /collectionType parameter. This parameter is not supported by the vs2008 service reference GUI.

Source: WCF Collection Type Sharing




回答4:


I finally found a way to do this. I found this link that pointed in the right direction but I will try to summarize.

  1. Make sure you add [CollectionDataContract] to your custom collection
  2. Add Service Reference through VS like you normally do
  3. Expand the service reference and look for the Reference.svcmap file
  4. Under the client options node you will see

<CollectionMappings/>

Replace it with the following xml.

<CollectionMappings>
  <CollectionMapping TypeName="Full.Namespace.Here" Category="List" />
</CollectionMappings>

  1. Right click on the Service Reference and click update. It should no longer generate that "proxy" class and will let you use your shared class.


来源:https://stackoverflow.com/questions/267113/how-to-implement-an-inherited-dictionary-over-wcf

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!