WSE 2.0: SoapService and XmlSerialization
If you cut your teeth building Web services with ASP.NET, then you've become quite
accustomed to the developer productivity that results from the automatic serialization
of types to/from the <soap:Body>. Simply
declare your method to accept and return the data type of choice, and the underlying
infrastructure automagically takes care of serialization for you.
Unfortunately, all of the examples within the WSE 2.0 documentation show methods declared
with the SoapMethodAttribute as taking a SoapEnvelope as an argument and returning
a SoapEnvelope as a return value. If
you're at all like me, programmatically creating your SOAP envelopes, along with the
<soap:Body>, isn't your idea of developer productivity, and you can't fathom
trading all of the power of ASP.NET and it's support of automatic serialization in
WebMethods for the supposedly "new and improved" feature set of WSE.
However, while working on my MSMQ transport provider for WSE 2.0, I came across a
poorly documented feature of SoapService. You
can declare your SoapMethod to accept and return custom data types, and the WSE infrastructure
will take care of serializing those types to/from the <soap:Body>, just like
a WebMethod in ASP.NET. For example,
you can derive from SoapService and implement your SoapMethod like:
class MySoapService : SoapService
{
[SoapMethod("SomeMethod")]
public MyReturnType
SomeMethod(MyArgs args)
{
}
}
The other side of this equation is equally true. SoapClient
provides two overloads each for SendOneWay and SendRequestResponse. One
of the overloads accepts an object as opposed to a SoapEnvelope, and will automatically
serialize the object into the <soap:Body> of the request, thereby providing
you an easy way to send custom data types between your SoapClient and the remote SOAP-based
service.
class MySoapClient : SoapClient
{
public MySoapClient(Uri
to) : base(to)
{
}
public MyReturnType
SomeMethod(MyArgs args)
{
return
this.SendRequestResponse("SomeMethod", args);
}
}
Once again, the beauty of the .NET Framework comes shining through in it's ability
to support XML serialization for any and all types, built-in or custom. You
don't have to give this one thought - it just works.
You can also see this demonstrated in some of the WSE 2.0 samples, notably TcpSyncStockService. Hopefully,
this will be more fully documented by release. Until
then, it's called out here in an attempt to make your life a little easier when working
with WSE.
Comments
- Anonymous
December 16, 2003
Hi, I was exicted to read about this inbuilt serialization with WSE 2.0 however, when I tried to implement it myself I get an error.When I try a "return SendRequestResponse " out of a function who's return type is, say, Dataset, I get an error stating that "Value of type 'Microsoft.Web.Services.SoapEnvelope' cannot be converted to 'System.Data.DataSet'."What am I doing wrong?Thanks for your help. - Anonymous
February 23, 2004
Hi, in general the concept sounds great. I tried to implement the idea but like the previous response sent by Fergal - it dosen't work. I tried it with a serializable class and with a standard dataset but the error is the same: <my class> cannot be converted to Microsoft.Web.Services.SoapEnvelope'
What have we missed out? - Anonymous
July 13, 2004
You can use the GetBodyObject method of the SoapEnvelope returned to coerce the body to the requisite type.
My dilemna is that I must deal with the raw envelope in order to avoid the dynamic assemblies generated by the XML serialization; if I call my proxy via a CCW from ASP (not ASP.NET) then it doesn not have the necessary permissions to acces the dynamic assemblies and the call fails in mscorlib with error 0x80070002.
All I care about is generating the body, How do I fill in the rest of the envelope, esp. in light of all of the other layered features in WSE 2.0? - Anonymous
July 14, 2004
FYI, I got around the dynamic assembly problem by dumping info out of the AppDomain; it turns out that in the COM Interop world, it is using the windows temp directory. I set permissions there and voila.
However, when I try to use GetBodyObject to coerce teh SOAP body into a class containing a single DataSet member (which is the paradigm provided by WSE 2.0's proxy generation), I get back any empty DataSet named "NewDataSet". It looks like the DataSet is getting constructed, but never filled... - Anonymous
August 02, 2004
Hmm.. I was able to make it work with a DataSet being returned (mind you, I did install the service pack just released for WSE 2.0 which might have had something to do with my success).
While I really appreciate the idea (and the hard work by the WSE folks), sadly, it appears that this will only work for a single argument. If you try and put two arguments on your web method, it will fail.
I think this makes sense, since the mechanism to pass arguments (from the client side at least) is really taking advantage of the SoapClient.SendRequestResponse() override that takes an object as its second argument (instead of a SoapEnvelope). I assume that this override simply asks the single object argument to serialize itself and then dumps it into the body of the SoapEnvelope.
What this means is that if you really want multiple arguments, you are going to have to serialize these yourself -- either by using a class that can be serialized as a single argument, or by using the SoapEnvelope directly. Personally, I'm thinking that the easiest would be to create a class that can be serialized since you can avoid the 1 or 2 lines of working with the SoapEnvelope Body.
In any case, this still leaves me a little "wanting" for the work of argument serialization to be handled by the infrastructure... Have I missed something?