chilltemp
chilltemp

Reputation: 8962

How to implement an inherited Dictionary over WCF

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

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:

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

Upvotes: 8

Views: 12535

Answers (4)

Shaun Bowe
Shaun Bowe

Reputation: 10008

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.

Upvotes: 1

chilltemp
chilltemp

Reputation: 8962

  • 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

Upvotes: 1

jezell
jezell

Reputation: 2532

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

Upvotes: 10

Marc Gravell
Marc Gravell

Reputation: 1062492

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>

Upvotes: 1

Related Questions