Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I stop my WCF generating ArrayOfString instead of string[] or List<string>

Tags:

wcf

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 ?

like image 279
Simon_Weaver Avatar asked Feb 03 '09 03:02

Simon_Weaver


2 Answers

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>()
});
like image 81
Fábio Batista Avatar answered Oct 10 '22 01:10

Fábio Batista


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);
like image 22
JulianM Avatar answered Oct 10 '22 02:10

JulianM