Fellow _MS RD_ and a software legend, Rocky
Lhotka posted an interesting entry on Message-based WebMethod "overloading" here.
In it, he recommends (and I quote) that service methods use a request/response
or just request method signature:
response = f(request)
or
f(request)
"request" and "response" are both messages,
defined by a type or schema (with a little "s", not necessarily XSD).
The actual procedure, f, is then implemented
as a Message Router, routing each call to an appropriate handler method depending
on the specific type of the request message.
I couldnt agree more. While easier to comprehend, the practice of passing parameters
to a Web Method call often sends the wrong messages across as _SOAP_ as just another
RPC. Ultimately, if you have no idea and dont even want to grok the innards of the XMLSerializer,
you would really think you are passing method parameters across or worse, think that
you are ferrying objects across the wire.
Therefore, I firmly believe that it is for the good of all if you explicity specify
that you are expecting a XMLDocument / XMLElement / XMLNode and you will dispatch
the same on your endpoint. What you do at your implementation (whether it is serializing from
a type to deserializing from one) is placed squarely at the mercy of the tools in
your hand or the knowledge in your head.
With the current state of tools (or lack there-of) today, I sit on the same camp as Aaron
Skonnard and Christian
Weyer (plus others, I believe) as I believe firmly in the contract-first approach.
Good devs should dig what is going on as close to the wire as possible, at
least once. Then they can use all the wizards and helpers they want.
This will help them understand better what is going on if they should need to troubleshoot
later (leaky abstractions) or find other ways to improve performance.
This is just something that my team and me here have gathered over the years esp when
I have got many developers out there working on the same solution offshore in different
countries.
While I agree that not many people out there enjoy or want to grok the angle brackets
and that the lack of tools out there is hugely to be blamed, tools make a productive
developer but not necessarily a proficient one.
Until today, I still come across developers that still think Web Services are still
about transferring an object across the wire. Having terms like "Web References" and
"proxies", even deemed to be more abstract and dev-friendly, does potray the wrong
ideas across.
I have always recommended younger developers who are interested in learning
about Web Services / ASMX and SOAP to try out the (just-now-defunct) Microsoft
SOAP Toolkit first before moving on. I find that to be a great interesting
way to learn about XML SOAP Services as abstractions are kept to a minimum. Another
great SOAP Toolkit in the face of Microsoft`s non-supporting stance of its own is Simon
Fell`s PocketSOAP.
Another fellow Regional Director, Bill
Wagner (who authored an very impressive book "Effective
C#") posted his solution to Rocky`s post here. I
have used the same sort of approach that Bill had documented before and it is
good and does serve its purpose. However (and please correct me if I am wrong),
it bounds the message contracts and datatypes tightly to the WSDL. If I am going to
add a third Request / Response pair of classes, it will render my initial WSDL invalid
(unless of course, if I am willing to add an additional endpoint)
I worked on a project before which specify that (newer) datatypes are to be added
in phases and therefore, we had to decouple the XSD from the WSDL (which is in accordance
with one of the best
practice of WSDL Deployments -- modular and the separation of concerns). Oh, by
the way, this is practiced in _Indigo_.
I don`t know if there is a way to decouple the XSDs from the WSDL in VS2003 today.
Even if there is, I am guessing it is a difficult hack at best. So, what I did was
to create the XSDs for each datatype as they come along and do a XSD import in my
WSDL. At my message exchange contract, I used an open
content type with xsd:any. Thereafter, I author my own WSDL with the help of Christian`s
and Thinktecture`s WSContractFirst.
Since the message and the datatype XSDs are all imported, the wsdl actually has a
small footprint now. With the wsdl /server switch, xsd:any becomes an XMLElement type.
For abstraction within my service, I changed it to an object in my implementation
detail.
Note: .NET 1.1`s WSDL.exe /server switch, in my mind, is still
fairly limited and comes with a couple of annoying things I didn`t like BUT I will
expand this in detail later.
[System.Xml.Serialization.XmlTypeAttribute([Namespace]:="urn:softwaremaker-n
et.swmstoreexchangemessages"]> _
Public Class Response
Public Result As Result
End Class
[System.Xml.Serialization.XmlTypeAttribute([Namespace]:="urn:softwaremaker-n
et.swmstoreexchangemessages")] _
Public Class Result
[System.Xml.Serialization.XmlElement(ElementName:="Book",
Type:=GetType(BookType),
Namespace:="urn:softwaremaker-net.swmstoreextendedtypes"), _
System.Xml.Serialization.XmlElement(ElementName:="CD",
Type:=GetType(CDType),
Namespace:="urn:softwaremaker-net.swmstoreextendedtypes"), _
System.Xml.Serialization.XmlElement(ElementName:="Anything",
Type:=GetType(AnyType),
Namespace:="urn:softwaremaker-net.swmstoreextendedtypes")] _
Public Any As Object
End Class
Public Function
ProcessRequest([System.Xml.Serialization.XmlElementAttribute([Namespace]:="u
rn:softwaremaker-net.swmstoreexchangemessages")] ByVal Request As Object) As [System.Xml.Serialization.XmlElementAttribute("Response",
[Namespace]:="urn:softwaremaker-net.swmstoreexchangemessages")] Response
`...
End Function
Once the consumer points to my wsdl (I turned off documentation of asmx by default),
all the XSDs are imported by VS2003 as well (and that is a good thing). The xsd:any
is still an XMLElement over at their end and we leave it as it is since we cannot
control what they do there. The consumer can choose to deal with the raw XML if they
want to OR do an xsd.exe imported.xsd /c and let the XMLSerializer do its (limited)
magic i-wink.
In this sense, no matter how many more datatypes I add or remove, my wire-contract
remains the same. I just let the endpoints serialize / deserialize the open-content
xml (xsd:any). In my project I had mentioned earlier, I have one asmx endpoint servicing
multiple consumers each sending different messages that conforms to different XSDs
(For example, GroupA sends BookType and GroupB sends the CDType as well as a
GroupC next time that sends a type that is unknown to me today). The thing I need
to take care of is to send the appropriate datatype xsd to the appropriate groups
so they can serialize / deserialize into the appropriate types. As you can see from
the code snippet above, at my asmx, I will just add any new datatypes that come along.
While, this may seem like too much compared to Bill`s solution above, it was necessary
to decouple the datatypes from my wsdl in that project and the XSD Editor (codenamed:
daVinci ) in VS.NET2003 was the best tool I had in hand at that time. Developers
are too comfortable with objects. This is obviously natural with the decade-long evolution
of Object-Orientation and the power of abstractions the OO tools and languages bring
today.
However, other factors like time, extensibility, standardization make abstractions
expensive...and among others, these are all factors that make up the principles of
the move towards Service-Orientation. Now, if we have to make and designed services
to be explicit, this means that devs need to know they are explicity invoking a service
across the wire, how far can the tools go to hide all the plumbings and yet not hide
the fact that the service is on the other side and not just a mere shadow
copy of a remoting object
that seems to reside on the same application domain.
Will Service-Orientation fail to hit the mainstream because of this I dont know.
I guess only time will tell.