Poking around the VS.NET CodeModel

Today was a pretty darn interesting day. I spent a big chunk of it writing a Visual Studio .NET add-in that uses the CodeModel to munge around with C# source code. The experience ran the gamut, from initial excitement at early successes, to frustration as limitations were encountered, to satisfaction when the goals of the project are finally met. It isn't too often that all of that happens all in one day.

One of my first weblog posts was about Web Service proxies. When WSDL.EXE creates the proxies, it uses fields rather than properties to expose the data elements of the proxy classes. This is unfortunate, because it mean that the proxies can't be used very easily with the .NET databinding architecture (which only works with properties). At various times I've poked around at potential workarounds to this problem, but never came up with a workable and satisfactory solution. As I poked at the CodeModel, it seemed to offer the solution I was looking for.

The idea was to use the CodeModel API to parse the generated proxy classes, converting public fields into private ones and exposing property getters and setters for the fields.  Initial experiments were promising - I quickly had working code that ran through the public fields of a class and changed them from public to private.  The CodeModel API was quite easy to use, and seemed to do what I needed.

“Seemed” is the key word. I hit my first major problem with fields that are annotated by XML serialization attributes. The CodeModel doesn't handle that too well. For example, changing the access modifier of an annotated field from public to private put the “private” keyword before the attribute, and left the public keyword in place (this works for a non-attributed field). I ended up working around it by completely removing the original public field and adding a new private one.

Adding the corresponding property getter and setter was pretty easy, but then I hit the next problem - adding attributes through the API doesn't work. The documentation implies that this should work for C# (the VB.NET CodeModel is read-only, but there's no such limitation documented for C#). It doesn't. This was the trickiest problem to figure out, but it turned out to not be very difficult to code. The EditPoint class let me copy the text of the attribute definition from the original field and paste it in front of the property definition, and that was a good enough solution for my needs.

So things were working pretty well on a small sample. But as I was testing the tool on a rather large WSDL file, I encountered a new problem - sometimes it would hang in the middle of execution. The hang occurred at random places in the WSDL, although it was almost always while calling the CodeClass.AddProperty method. Suspecting maybe a timing or race condition, I threw a Thread.Sleep(100) in between each class, and haven't seen the problem since. I hate hacks like that, but I'm living with it for the moment.

The final challenge was figuring out where to plug the add-in into the project system. My preference was to provide a context menu option directly on the web reference which would trigger the conversion of fields into properties. Again, it wasn't hard - the trick was figuring out which Command Bar to add the menu item to (“Folder“, in this case).

And that was pretty much it. It took less than a day to write, is less than 400 lines of code, and now I have data-bindable web service proxies. I hope Microsoft continues to flesh out and fix the CodeModel stuff in VS.NET. Bugs and limitations aside, it's pretty sweet, and quite powerful.

And of course, I hope Microsoft changes WSDL.EXE to generate properties instead of field. Or changes databinding to work with fields. I'll keep my fingers crossed for Whidbey.

 

3 Comments

  • Thanks !!

    I was planning to do something similar over the weekend. Your post will definitely save me a few hours.



    BTW Are you going to release the code?? That would save me a few more :)



  • Actually, originally I had looked into doing it exactly as you suggest, Atif. But I wasn't able to figure out where to hook in to the process. Thanks for the pointer to ServiceDescriptionImporter. I'll definitely look into it.

  • My fingers got itchy so I had to try the theory out. Wanted to let you know that it worked without any issues (although I only tested against a simple WSDL). I made alteration to my WebServiceClientGenerator.cs but I haven't checked in the changes. If you want to check it out, download the latest release from Workspace (which contains the source code) and then send me a mail. I'll then ship the modified file to you.

Comments have been disabled for this content.