12

I am having a minor problem with WCF service proxies where the message contains List<string> as a parameter.

I am using the 'Add Service reference' in Visual Studio to generate a reference to my service.

   // portion of my web service message
   public List<SubscribeInfo> Subscribe { get; set; }
   public List<string> Unsubscribe { get; set; }

These are the generated properties on my MsgIn for one of my web methods. You can see it used ArrayOfString when I am using List<string>, and the other takes List<SubscribeInfo> - which matches my original C# object above.

[System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false)]
public System.Collections.Generic.List<DataAccess.MailingListWSReference.SubscribeInfo> Subscribe {
    get {
        return this.SubscribeField;
    }
    set {
        if ((object.ReferenceEquals(this.SubscribeField, value) != true)) {
            this.SubscribeField = value;
            this.RaisePropertyChanged("Subscribe");
        }
    }
}

[System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false)]
publicDataAccess.MailingListWSReference.ArrayOfString Unsubscribe {
    get {
        return this.UnsubscribeField;
    }
    set {
        if ((object.ReferenceEquals(this.UnsubscribeField, value) != true)) {
            this.UnsubscribeField = value;
            this.RaisePropertyChanged("Unsubscribe");
        }
    }
}

The ArrayOfString class generated looks like this. This is a class generated in my code - its not a .NET class. It actually generated me a class that inherits from List, but didn't have the 'decency' to create me any constructors.

    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
    [System.Runtime.Serialization.CollectionDataContractAttribute(Name="ArrayOfString", Namespace="http://www.example.com/", ItemName="string")]
    [System.SerializableAttribute()]
    public class ArrayOfString : System.Collections.Generic.List<string> {
    }

The problem is that I often create my message like this :

client.UpdateMailingList(new UpdateMailingListMsgIn()
{
    Email = model.Email,
    Name = model.Name,
    Source = Request.Url.ToString(),
    Subscribe = subscribeTo.ToList(),
    Unsubscribe = unsubscribeFrom.ToList()
});

I really like the clean look this gives me.

Now for the actual problem :

I cant assign a List<string> to the Unsubscribe property which is an ArrayOfString - even though it inherits from List. In fact I cant seem to find ANY way to assign it without extra statements.

I've tried the following :

  • new ArrayOfString(unsubscribeFrom.ToList()) - this constructor doesn't exist :-(
  • changing the type of the array used by the code generator - doesn't work - it always gives me ArrayOfString (!?)
  • try to cast List<string> to ArrayOfString - fails with 'unable to cast', even though it compiles just fine
  • create new ArrayOfString() and then AddRange(unsubscribeFrom.ToList()) - works, but I cant do it all in one statement
  • create a conversion function ToArrayOfString(List<string>), which works but isn't as clean as I want.

Its only doing this for string, which is annoying.

Am i missing something? Is there a way to tell it not to generate ArrayOfString - or some other trick to assign it ?

Tunaki
  • 132,869
  • 46
  • 340
  • 423
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689

4 Answers4

7

Any .NET object that implements a method named "Add" can be initialized just like arrays or dictionaries.

As ArrayOfString does implement an "Add" method, you can initialize it like this:

var a = new ArrayOfString { "string one", "string two" };

But, if you really want to initialize it based on another collection, you can write a extension method for that:

public static class U
{
    public static T To<T>(this IEnumerable<string> strings)
        where T : IList<string>, new()
    {
        var newList = new T();
        foreach (var s in strings)
            newList.Add(s);
        return newList;
    }
}

Usage:

client.UpdateMailingList(new UpdateMailingListMsgIn()
{
    Email = model.Email,
    Name = model.Name,
    Source = Request.Url.ToString(),
    Subscribe = subscribeTo.ToList(),
    Unsubscribe = unsubscribeFrom.To<ArrayOfString>()
});
Fábio Batista
  • 25,002
  • 3
  • 56
  • 68
3

Too late but can help people in the future...

Use the svcutil and explicitly inform the command line util that you want the proxy class to be serialized by the XmlSerializer and not the DataContractSerializer (default). Here's the sample:

svcutil /out:c:\Path\Proxy.cs /config:c:\Path\Proxy.config /async /serializer:XmlSerializer /namespace:*,YourNamespace http://www.domain.com/service/serviceURL.asmx

Note that the web service is an ASP.NET web service ok?!

Fergara
  • 929
  • 8
  • 15
  • Is this essentially the difference between a "service reference" and a "web reference"? Does specifying service reference use the DataContractSerializer, and web reference use the XmlSerializer? – Samuel Meacham Nov 04 '11 at 19:17
3

I prefer not to return generic types across a service boundary in the first place. Instead return Unsubscribe as a string[], and SubscriptionInfo as SubscriptionInfo[]. If necessary, an array can easily be converted to a generic list on the client, as follows:

Unsubscribe = new List<string>(unsubscribeFrom);
Subscribe = new List<SubscriptionInfo>(subscribeTo);
JulianM
  • 1,072
  • 10
  • 19
1

If you are using VS 2008 to consume service then there is an easy solution.

Click on the "Advanced..." button on the proxy dialog that is displayed when you add a Service Reference. In the Collection Type drop down you can select System.Generic.List. The methods returning List should now work properly.

(Hope this is what you were asking for, I'm a little tired and the question was a tad difficult for me to read.)

Sailing Judo
  • 11,083
  • 20
  • 66
  • 97
  • 1
    i'm tired too! i keep writing too long questions. sorry if it wasnt clear. i think you understood it though! unfortunately your suggestion doesn't work - it INSISTS on making a new ArrayOfString class for string whatever i choose 4 that! perhaps its getting confused thinking string is a value type? – Simon_Weaver Feb 03 '09 at 04:28
  • hmm. i'd have to test it out. unfortunately my work schedule is a little hectic today – Sailing Judo Feb 03 '09 at 17:23