.NET Web Service Clients Give You No Choice

(How’s that for a provocative post title? I can’t wait for the flames!)

I’m doing a lot of web services interop work with Java and .NET. I’ve got a Windows Forms application calling a Java web service over SOAP. The Java application behind that web service “does stuff,” and then turns around calls a .NET web service via HTTP GET. It’s a lot of fun stitching this stuff together. More or less.

I discovered several weeks ago that Microsoft’s wsdl.exe tool immediately barfs when it encounters a <choice> element in the schema. It does this because, if you think about it a little bit, a <choice> is really hard! The <choice> element is the XML equivalent of C’s union keyword. It allows multiple structures to be defined as valid in a certain space, but which structure will actually be there is unknown until run time. Generating C# code for something like that is rought.

Here’s a way they could have done it, though: Take the elements actually in the <choice> and give them a common interface. Then you can put the interface in place of the classes generated for the <choice>’s child schemas. You can then just recurse through all of the structures allowed by the <choice> element, and generate the code as usual. At run-time, you can figure out which structure you have using a simple is statement.

Why use an interface? Imagine that, somewhere in your schema, you have a <choice> that looks like this:

<choice>
  <element type="e1" />
  <element type="e2" />
</choice>

The classes E1 and E2 get generated from the types e1 and e2, respectively, and that their base class is given the name B1. Then, later on, you have a <choice> that looks like this:

<choice>
  <element type="e2" />
  <element type="e3" />
</choice>

So what do you do? You either need to generate a different class for e2 schema type here, or the generated class E2 has to have multiple base classes. Since you can’t do multiple inheritence in .NET, implementing multiple interfaces is the next best thing. So each <choice> gets an interface generated for it.

It all seems to work in my head, but admittedly I haven’t tried it. Watchu think?