views:

2261

answers:

3

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 ?

A: 

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
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
hmm. i'd have to test it out. unfortunately my work schedule is a little hectic today
Sailing Judo
+3  A: 

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  A: 

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