So, You Wanna be a Plumber?

This morning while scanning various newsgroups, I noticed a post in the WSE newsgroup that read:

hi, could anyone tell me how to implement ws-transaction in .net?

Disclaimer: The following "comment" is a view based on my experiences that were formulated before, during, and after implementing a WS-* specification. I can't guarantee that these steps will work in every instance but they are, in my opinion, pretty consistent; you'll get a fair way through your implementation if you read these tips & tricks.

So, you wanna be a plumber? Well, much like washing your hair, the following list forms a "wash, rinse, repeat" series of steps to help guide you while implementing a WS-* specification with WSE:

  1. Block-reserve many nights away from your spouse. You'll need them.
  2. Download the specification and its associated schema/WSDL (if available). Print these documents and tape them to your bedroom wall/ceiling. You will be living, breathing, eating, sleeping this specification for the next few weeks.
  3. Now, read the specification five times.
    • If something in the specification is unclear, read the specification again.
    • If that doesn't work, assign yourself a "question monkey"; preferably someone who knows a heck of a lot more than you do. (I would suggest either Benjamin Mitchell or Christian Weyer. Both are class acts and very helpful. And no, I don't consider them monkeys).
    • If that doesn't work, e-mail a question(s) to its authors (preferably the editor). This should be your last resort, however. I'm pretty sure they don't really appreciate being asked questions like "what does element<xyz> do?" when it's clearly defined in the specification.
  4. Google the term, "WS-<specification>" and read everything you find listed online.

Now, you're ready to start cutting code. Here's where the development process gets a little grey since specifications can vary greatly.

  1. Crank up God's IDE: Visual Studio .NET.
  2. Do the VS.NET "dance": File -> New -> Project (ctrl + shift + N)
  3. Label the project, Microsoft.Web.Services.<specification>
  4. Do the WSE "dance": Right-click the References folder -> Add references -> Microsoft.Web.Services.dll. Alternatively, you can use the WSE Setttings VS.NET utility.
  5. Define a singleton called WS<specification>. This class will encapsulate all the URIs and SOAP action strings that you will be using throughout your code.

If the specification outlines numerous SOAP headers:

  1. Create two custom filters: <specification>InputFilter, which derives from Microsoft.Web.Services.SoapInputFilter and <specification>OutputFilter, which derives from Microsoft.Web.Services.SoapOutputFilter.

For more information, see my article on creating a custom output filter (for Groove Workspace). It’s a shameless plug, but what the Hell, eh?

If the specification outlines an endpoint:

  1. Create two sub-folders in your project; one called "Configuration" and one called "Policy". This is where you'll define any/all associated configuration and custom policy assertions for your specification implementation (very important).
  2. Create a class called <purpose>Service that derives from Microsoft.Web.Services.Messaging.SoapService.
  3. Auto-generate a client proxy via WseWsdl.exe called <purpose>ServiceClient. This class derives from Microsoft.Web.Services.Messaging.SoapClient.
  4. If the specification outlines an endpoint and WSDL port operations, override the GetDescription method for your SoapService-derived class. This is a hook into the WSDL auto-generation process for ASMX. Use it because it's vital for achieving interoperability. (Oh, and try to generate WS-I BP v1.0a-compliant WSDL. That helps also.)
  5. Repeat steps 1-4 for each endpoint listed in the specification.

If the specification schema (<specification>.xsd) defines new endpoint references (EPRs):

  1. Create a class called, <element-name> that derives from Microsoft.Web.Services.Addressing.EndpointReference.
  2. Override GetXml and LoadXml. These methods will forever-be your nearest and dearest friends. They will help you move from the OO world to the XML world and back again.
  3. Define implicit and explicit operators to convert to/from a System.Uri type. This is a nice feature from a proxy client POV.

If the specification schema (<specification>.xsd) defines extensible/open elements (see xsd:any and xsd:anyAttribute):

  1. Create a class called <element-name> that dervices from Microsoft.Web.Services.Xml.OpenElement.
    • For more information on Microsoft.Web.Services.Xml.OpenElement, see my blog post.
  2. Override GetXml and LoadXml. Remember, these methods are your nearest and dearest friends. They will help you move from the OO world to the XML world and back again.

If the specification schema (<specification>.xsd) defines union elements (see xsd:union) where one of the member types doesn’t map well to a class in the .NET Framework:

  1. Create a utility class that will convert the XML representation of this type into a .NET type. (For example, NonNegativeDuration in the WS-Eventing specification.) You may want to check out some of the types listed inSystem.Runtime.Remoting.Metadata.W3cXsd2001 (in mscorlib.dll) to help out with these conversions.
  2. Incorporate this class into your GetXml method logic where it’s needed. (You’ll thank me later.)

Now, you're ready to start testing.

  1. Go download .NET Web Service Studio v2.0 from GDN. There are tons of other WS-related testing suites out there. I prefer this one because it uses WSE directly.
  2. Hit your endpoints with every imaginable piece of SOAP-based "goop" you can. Make sure all SOAP faults are correctly generated in accordance with the specification, which may vary (read "OPTIONAL" in RFC 2119).
  3. Repeat step 2 until your endpoint is robust.
  4. Attempt to use the endpoint from your proxy client (<specification>ServiceClient). Hope and pray while testing.
  5. (Optional) Push the bits/source into the public domain: GDN workspace, SourceForge, or VaultPub at SourceGear (makers of the excellent, excellent, excellent SourceGear Vault).
  6. Report your findings and attempt to deflect any rotten tomatoes thrown your way (in case of a bad implementation).

Remember:

  • Family comes first; implementing a specification can & will consume many of your cycles.
  • WS-* specifications are for plumbers. They can be hard to grok, so be wary before embarking.
  • VS.NET is the best IDE, period.
  • WSE is part of God's service-oriented arsenal (SOA).
  • Always pillage before you burn.

Good luck!

PS: Hmm, I wonder if these tips & tricks constitute good custom FxCop rules for WSE-related development?