Make Your Web Service Proxies Return List<T> Using RegEx (And The Regulator) - Hilton Giesenow's Jumbled Mind

Hilton Giesenow's Jumbled Mind

the madness that is...

News

This is my little spot in cyberspace where you will find a collection of random (but mostly software-related) thoughts and ideas that are frightening in their shining brilliance (or something like that ;->).
 
Please enjoy your stay and feel free to Contact Me.
 
Microsoft MVP

.Net Links

BlogRoll

Misc. Links

Syndication

Make Your Web Service Proxies Return List<T> Using RegEx (And The Regulator)

 

[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...]

Posted: Oct 31 2006, 10:34 PM by hiltong | with 8 comment(s)
Filed under: ,

Comments

Hilton Giesenow's Jumbled Mind said:

Incidentally, someone acutally commented recently on one of my WCF tutorials about this, but I was working
# November 1, 2006 12:49 PM

Senkwe said:

Hmm, I only skimmed the post so forgive me if I'm being dum, but it sounds like you're defeating the purpose of the concept of web services, namely "interoperability". What's a PHP client going to do with a List?
# November 5, 2006 12:01 AM

hiltong said:

"There are no stupid questions, only stupid answers", apparently, so I hope I'm not going to give a 'dum' answer ;-)...

All of the work above is only going to make sense within _the client_ itself. It does not affect the web service itself at all. In fact, in this usage we don't even have access to the service code. It has no knowledge of what the web service is written in, as long as generates a standard proxy. This is only adjusting the proxy on the client - the resource.cs file that is generated when you Add -> Web Reference, to make using the proxy code easier and more functional.

HTH,

- Hilton

# November 5, 2006 12:33 AM

svyas said:

Perfect Solution!!! Is MS planning to do something keeping in mind that works well for .Net Client.

# February 2, 2007 5:44 AM

Hilton Giesenow's Jumbled Mind said:

I posted a solution a little while ago to have an existing .net Web Service client proxy return a List

# February 6, 2007 4:48 PM

mrK said:

Excellent article

<i>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!)<i>

To avoid changes being overwritten why dont you just create partial classes for your proxies. Put all the changes in the partial class

# August 8, 2007 8:55 AM

Sachin Wandhare said:

Above workout can be done with svcutil

svcutil localhost/.../MyService.svc /ct:System.Collections.Generic.List`1  /out:MyServiceProxy.cs /namespace:*,MyServices.Service /config:app.config /async

I am facing some inconsistent beahviour with this option and service referece described above.

In my application, I am able to generate generic list return type with above approach but one of my service is still giving me array type of return even though it has got methods which return List<CustomType>.

Please provide me solution for this problem

# September 29, 2008 6:01 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Enter the numbers above: