Or, a reasonably simple technique to build polymorphic web services.
I participated in a discussion recently about how to build a web method that can accept and return multiple schemas.
I’d like to build this web service API:
[WebMethod]
Response ProcessRequest( Request packet )
{
// elided.
}
With a little help from Xml attributes in .NET, this is possible. It’s not quite as elegant as I’d like (yet) but it still saves time and is quite resistant to breaking changes at the server.
Start by defining the abstract Request and Response classes:
public abstract class Request
{
public abstract Response ProcessMe();
}
public abstract class Response
{
}
Now you can fill in the rest of your web method body:
[WebMethod]
public Response ProcessRequest( Request packet )
{
return packet.ProcessMe();
}
Your web service implementation is done. Following the example from OO, you simply create new classes derived from Request and Response. Here are two small examples. (You can download the full code from the link below. The snippets are illustrative, and not meant to be the full implementation.)
This pair returns the sum of two numbers:
public class AddRequest : Request
{
public AddRequest()
{
}
private int left;
public int Left
{
// ...
}
private int right;
public int Right
{
// ...
}
public override Response ProcessMe()
{
return new AddResponse( Left, Right );
}
}
public class AddResponse : Response
{
public AddResponse()
{
}
public AddResponse( int left, int right )
{
Result = left + right;
}
private int result;
public int Result
{
// ...
}
}
And, just to show you can do this with any multiple types of requests, here’s an example where the request has no parameters. It returns the current time:
public class TimeRequest : Request
{
public TimeRequest()
{
}
public override Response ProcessMe()
{
return new TimeResponse();
}
}
public class TimeResponse : Response
{
public TimeResponse()
{
ThisTime = DateTime.Now;
}
private DateTime thisTime;
public DateTime ThisTime
{
// ...
}
}
Now comes the part that is a little less than elegant. What I’ve done so far does not work as a web service. The XmlSerializer does not know how to serialize the derived classes (TimeResponse and AddResponse) from the server. In addition, the generated WSDL document will not contain the definition for any of the derived request or response types. (For those of you with a C++ background, this problem is analogous to slicing objects when you passed types by value.) You can fix that by adding attributes on the Request and Response classes to note what derived types should be included in the serialization schema:
[ XmlInclude( typeof( AddResponse ) ) ]
[ XmlInclude( typeof( TimeResponse ) )
public abstract class Response
[ XmlInclude( typeof( AddRequest ) ) ]
[ XmlInclude( typeof( TimeRequest ) ) ]
public abstract class Request
Finally, you need to add an XmlElement attribute to the web method:
[WebMethod]
[return: XmlElement("Response")]
public Response DoRequest( Request r )
Anytime you add a new message, you do a few small steps:
Create your new request schema, by deriving a class from Request.
Create your new response schema, by deriving a class from Response.
Add XmlInclude attributes for the two new schemas on the Request and Response type definitions.
Final note: Josh Holmes, one of my business partners is one of the best people around on XML schemas. He’s going respond to this post within a week, expanding on the sample to serialize more complicated objects, including collections.
The full sample
Download and play with it yourself
Josh's blog
Look here for the followup
Rockford Lhotka comments
After my original post, Rockford Lhotka added his thoughts