Monday, October 18, 2004

Generating WSDL Without ASP.NET

Because wsdl.exe generates classes that don't look exactly like we want them to, I've set up a process at my client to generate custom proxies. Right now, we do this as a partly manual process - we've got a simple script that downloads the WSDL from a set of web services we've developed, then generates the default proxies by running wsdl.exe against them. Then we make some simple hand edits against them to make them look how we want them to.


There are two problems with this process:



  1. It requires hand editing the files every time we want to make a change.

  2. The turnaround sucks - we have to deploy the web service somewhere before we can get the WSDL.

The first one I'll fix later. It's pretty easy to do right now, since most of the changes are just removing code. At some point I'll cobble together some sort of sed script or something.


The second one is sort of a pain at the moment, though. We're using Continuous Integration, which is great because it means every time someone makes a change, we get a fully tested and installed version of the product running on a web farm about twenty minutes later. But because that's currently the only way we have of deploying (setup is somewhat complex), it means that if someone adds a [WebMethod], they have to wait 20 minutes for the deployment before they can download the wsdl and update the custom proxies.


I thought it would be pretty nice if instead there was some way to generate WSDL directly from the web service assemblies. Well, after poking around a bit with Reflector, and with a little help from Tim, I'd figured out the incredibly easy code to do exactly this. Assuming you have a web service class called MyService, the following code will spew the WSDL for it to the command line:


ServiceDescriptionReflector reflector = new ServiceDescriptionReflector();
reflector.Reflect(typeof(MyService), "http://localhost/vdir/Foo.asmx");



if (reflector.ServiceDescriptions.Count > 1){
  throw new Exception("Deal with multiple service descriptions later");
}


XmlTextWriter wtr = new XmlTextWriter(Console.Out);
wtr.Formatting = Formatting.Indented;
reflector.ServiceDescriptions[0].Write(wtr);
wtr.Close();


The URL that appears in the call to Reflect is not used except to populate the address element in the WSDL.


It'll be the work of minutes to roll this code up into a little EXE that takes the class name on the command line. I'll probably add an option to reflect over all classes in an assembly, looking for anything with a [WebMethod] attribute on it somewhere and emitting that, too.

14 comments:

  1. Wow, very cool. Gotta love Reflector.



    Depending on the changes that you want to the proxy, you can customize the generation of those using the ServiceDescriptionImporter class. That's the code that you point to a WSDL file and it generates a CodeDom tree with the proxies. That tree can programmatically manipulated, then spat out into a file. I've done it, it's not too difficult.

    ReplyDelete
  2. Right - I've seen this e.g. in Christian Weyer's contract first stuff. I may wind up going there, but the grunginess of CodeDOM has me wary. If you say it's not so bad, perhaps I'll have to have a look at it sooner rather than later.

    ReplyDelete
  3. What kind of changes do you want to make? I'm sure some changes are easier than others.

    ReplyDelete
  4. I was hoping that VS 2005 would generate partial proxy classes...

    ReplyDelete
  5. The changes we need to make are basically to rip out a bunch of types, and to replace them with types that are already defined elsewhere.

    ReplyDelete
  6. When you say replace, do you mean rip out the source and put in new source? Or rip out the source and add a reference to an external assembly? The former would probably be tough (unless you already have your new source in CodeDOM format - not likely). The latter, however, is pretty easy.

    ReplyDelete
  7. In our case, I'm ripping out old source and replacing it with a using statement.



    Actually, I just got done writing it. As you suggested it might be, it was dead easy - took about an hour. :)



    ReplyDelete
  8. right on. thanks for this snippet!

    ReplyDelete
  9. I need to get the complextype element information from WSDL Document without creating proxy.

    can i do this with ServiceDescriptionReflector?



    ReplyDelete
  10. Why not just load the WSDL directly into an XmlDocument and then use XPath or something to query it for the information you want?

    ReplyDelete
  11. Great stuff! Really helped me with my new project (SOAP over XMPP). XMPP is a XML-based IM protocol (the same Google Talk uses), so this really helps with getting the chore of generating WSDL out of the way. Now I just need to figure out how to wrangle .Net into generating and consuming SOAP requests :).

    ReplyDelete
  12. Pingback from A service serialization pattern that….failed, but could beget a cool tool « Techkn0w

    ReplyDelete
  13. Check out

    http://www.pluralsite.com/wiki/default.aspx/Craig/RebuildingWsdlExe.html

    for at complete implementation :)

    ReplyDelete
  14. I've created a small commandlinetool which can generate a WSDL file from a compiled c# assembly (dll) which contains one or more WebServices.

    Download can be found at http://wsdlgenerator.codeplex.com

    Any hints and tips are welcome !

    --Stef

    ReplyDelete