[Update: With Visual Studio 2008 there is now a setting in the IDE to do this. Check out From WCF Services" href="http://hilton.giesenow.com/archive/2008/05/21/using-generic-list-lt-t-gt-from-wcf-services.aspx" mce_href="http://hilton.giesenow.com/archive/2008/05/21/using-generic-list-lt-t-gt-from-wcf-services.aspx">this post for more]
We're working on a project at the moment that requires us to interact with a very rich third party Web Services layer that returns, amongst other things, a number of 'collections' of custom objects. I say collection loosely because in effect any grouping of items, when defined in XML (XSD effectively), is translated into a simple sequence, regardless of the original .net data type. This means that no matter whether you are storing your objects in an actual array, ArrayList, custom collection (":CollectionBase"), or even one of the new Generic types (e.g. List<T>, SynchronizedCollection<T>, etc.) the final proxy on the client will always revert to a standard array.
This is a pity because, while arrays have been with us for, well, ever, they are not exactly the most fully-featured basket we have (hence the new types modern languages contain!). In fact, aside from the .net types (e.g. ArrayList), the new .net 2.0 generic datatypes, such as List<T>, have even richer new features. Some of these are shared with 2.0 System.Array, but the List<T> options are cleaner and there are more of them. Incidentally, Ken Getz has a great article one some of these in a recent MSDN Magazine article Advanced Basics: Predicates and Actions - September 2006).
Now, the very first thought that went through my mind when we started looking at this (besides how to do it) was whether or not to edit the auto-generated proxy. These proxies do, after all, state "Changes to this file may cause incorrect behavior and will be lost if the code is regenerated". That said, editing them is not all that uncommon. I've done it myself when I wanted to add basic authentication (i.e. DefaultCredentials) to a proxy that would only ever get used in an intranet; I've now got a better way - more hopefully coming soon - but back then this worked fine). What it does mean, though, is that one wants to :
a) Minimise the amount of actual work required in the proxy because one needs to do it every time the proxy needs to be re-generated (in our case at least once a week at the moment!) and
b) Make sure one remembers to do it every time. This can be 'assisted' by the fact that the solution will probably not compile if you forget ;->.
After a bit of thought, as well as having listened to a great DotNetRocks show with Roy Osherhove recently (Roy Osherove on Regular Expressions), I decided RegEx would help, so I downloaded Roy's awesome free tool, The Regulator. Not having played with RegEx in a while, I had forgotten a lot of stuff, but the tool is nicely featured so it helped me figure out what I needed relatively quickly. I also had a hand from one of my colleagues (thanks Trevor :->) who pointed me in the right direction - literally, as you will see.
What we need to do is to match occurrences of Whatever[] and to convert them to List<Whatever>. So, a simple RegEx to locate these is:
(\w+)\[]
However, this won't do the replaces, so we need to name the items, like so:
(?<ArrayType>(\w+))\[]
With the Replacement specified as:
List<${ArrayType}>
Now, this works, but it works too well. We replacing all our custom object type arrays in the proxy, but we need to remember that the basic web reference proxy contains multiple items, including not only .net-language versions of the XSD elements (DTOs, if you will), but also wrappers for the actual web service SOAP calls, e.g.:
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("[Namespace]", RequestNamespace = "[Namespace]", ResponseNamespace = "[Namespace]", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public [SomeResponse] [SomeMethod]([SomeRequest] [request])
{
object[] results = this.Invoke("[SomeMethod]", new object[] {
[request]});
return (([SomeResponse])(results[0]));
}
And, you might notice that these contain... arrays of a type (object). We can use this RegEx, but it would conflict with my earlier recommendation (a), that the work be simple because we need to redo our work every time we regen. As a result, we want to improve our RegEx expression to try and ignore these patterns as well. If we look at the proxy methods, we can see common patterns - the variable is always defined as 'object[] results' and the parameter is always defined as 'object[] {'. Using this information, we can amend our RegEx to ignore these instances. To do this, I ended up using negative lookaheads (i.e ignore those patterns succeeded by the matching pattern). I've used the \w+ pattern with a lookahead on ' results' and the '[]' pattern with a lookahead on ' {', with the following result:
(?<ArrayType>(\w+))(?!\[]\sresults)\[](?!\s\{).
Now all we need to do is to run this against our proxy and viola we have a web service proxy that returns List<T> instead of arrays. In addition, a nice feature of The Regulator is that all of the work can be saved as .express file, which means it can be kept in your source control as well (just remember to copy the new proxy in before running). Using this, we've met the two goals above (it's quick and we can't compile if we forget to do it), and we also got to brush up our RegEx.
[Incidentally, WCF still generates the proxies using Arrays. If anyone from the team reads this (;->), perhaps consider it as a suggestion...]
Well, I finally got to the last technology in the .Net 3.0 (aka WinFX) stack - WPF. I've got 2 last-minute sessions coming up at Tech Ed Africa, which I'm very excited about, and the one is of course on Windows Presentation Foundation.
This morning I'm off ill, but couldn't help work on my decks a bit. While searching for a nice definition of XAML, I ended up at Wikipedia, but was not too impressed with the existing definition. Despite the common misconception (largely brought about by the original name - Xml AVALON Markup Language, apparently), XAML is mostly associated directly with WPF, but this is definite oversight because it extensively used in Windows Workflow Foundation. In actualily, XAML is xml object definition language, and Peter Himschoot has a very interesting article on the Microsoft site called Amazing XAML that covers this nicely, and shows how XAML can be used to create and define a maze for a Zork-type game.
Well, the long and the short of the Wikipedia side of things is that I ended up editing the definition quite a bit, and hopefully it makes more sense now and is more reflective. It was also interesting to get to work with the innards of the Wikipedia system (how many of us actually end up contributing?). Incidentally, while editing the XAML entry, I decided to add a very quick one for XAMLPad, which can be found here.
I'm curios of those who read this who else has made an wikipedia contributions? If you've CRUD'ed an entry, let me know.