Wednesday 20 February 2008

Dynamic SOAP Adapters

Following on from my previous post concerning dynamic HTTP adapters, this time I'll extend the same concept to the SOAP adapter. Many of the techniques previously presented are still applicable but the SOAP adapter has it's own little idiosyncrasies which once explained are easy enough to overcome.

The first issue is that of a proxy. In traditional .NET applications, when using a web service it is typical to add a web reference which, under the covers will generate a proxy to the remote service based on its WSDL definition. This is also what would happen if you add a web reference in your orchestration and use a static SOAP send port. However, for a dynamic send port we don't want to create a web reference - we'll generate the proxy manually using the wsdl.exe tool as follows:

C:\>wsdl /l:CS /o:c:\temp\wsdl http://localhost/CalcWebService/CalcService.asmx?wsdl
Microsoft (R) Web Services Description Language Utility
[Microsoft (R) .NET Framework, Version 2.0.50727.42]
Copyright (C) Microsoft Corporation. All rights reserved.
Writing file 'c:\temp\wsdl\CalcService.cs'.

Here we have generated a proxy class from the WSDL of the web service. Add this to a new Class Library project. Ensure the resulting assembly is strong named and add it to the GAC. Note that the WebMethod to be called should have a signature as below that will accept an XML message and return one too. If an alternative method signature is required it will be necessary to update the proxy class to act as an facade between the required signature and the web service one.

[WebMethod]
public XmlNode MyMethod(XmlNode Message)

As with the HTTP example, we'll be invoking a dynamic adapter from an orchestration, setting adapter properties to change its behaviour at runtime. Again, add a request/response send port to the orchestration port surface and mark its type as Dynamic. The only other configuration required on the port is to specify the pipelines to be used, in this case XmlTransmit and XmlReceive. Connect up the request and response operations to the relevant send and receive shapes in the orchestration. Note that the operation on the send port should have the same name as the WebMethod being called.

Prior to sending the message set the following send port properties:

// Assign message
msgSendRequest = msgReceiveRequest;


// Set base properties
MySendPort(Microsoft.XLANGs.BaseTypes.Address) =
                     "
http://localhost/CalcWebService/CalcService.asmx";
MySendPort (Microsoft.XLANGs.BaseTypes.TransportType) = "SOAP";

// Set HTTP adapter specific properties
msgSendRequest(BTS.SOAPAction) = "Add";
msgSendRequest(SOAP.MethodName) = "Add";
msgSendRequest(SOAP.AssemblyName) = "CalcTest.CalcWebServiceProxy.CalcService,
    CalcWebServiceProxy, Version=1.0.0.0, Culture=neutral,
    PublicKeyToken=b713f1108bae3109";
msgSendRequest(SOAP.TypeName) = "CalcTest.CalcWebServiceProxy.CalcService";

Some important points to note here:

  1. The TransportType is a means of informing BizTalk which adapter you want to use. If it is omitted the prefix of the Address (in this case http://) will be used to query an alias table to lookup the adapter to use. By default this will use the HTTP adapter, therefore we set the Microsoft.XLANGs.BaseTypes.TransportType property to "SOAP" to ensure that the SOAP adapter is targeted.
  2. The BTS.SOAPAction and the SOAP.MethodName are both set to the name of the WebMethod being invoked.
  3. The SOAP.AssemblyName must be populated with the details of the class name and assembly of the proxy to be used. This value takes the form Namespace.ClassName, Fully Qualified Assembly Name. Set this to the details of the proxy assembly created and GAC'd earlier.
  4. The SOAP.TypeName must also be set to the full name of the proxy class i.e. Namespace.ClassName. Not sure why this is required again but if it is not set an error occurs.

OK, now you should be good to go. Build, deploy and (hopefully) watch those SOAP requests flying to and from your web service. Whilst it's a little more fiddly to setup that the HTTP equivalent, using a dynamic SOAP adapter is a really powerful technique for creating flexible and extensible BizTalk applications.

Jon Fancey has taken this a step further by creating a more generic SOAP router. Check it out.

14 comments:

Unknown said...

Microsoft.XLANGs.BaseTypes.TransportType is only available under Biztalk 2006 R2 and not under Biztalk 2006 ?

Anonymous said...

Hi,

My Id is imroze.mohammed@gmail.com.

Can You please provide me the code. I am having some problems regarding the Webservice output redirection to a message.

Thanks&Regards,
Imroze Mohammed

Anonymous said...

Hi,
I've followed the steps what you've mentioned.But i've ended up with error. Please clarify me.

Error:
A message sent to adapter "SOAP" on send port "DyanmicCallToWebService_1.0.0.0_DyanmicCallToWebService.DynamicOrch_Port_3_0f5c1cda538e8e02" with URI "http://localhost/TestService/Testing.asmx" is suspended.
Error details: Failed to lookup method "Operation_1" in the type "Testing".
Possible reasons:
1) The method does not exist or it is not public.
2) The method does not have attribute

Mark said...

Hi,

This error would imply that the operation name on the port does not match the method name on the service that you are trying to call. In the sample there are 3 things I have set to the name of the method being called:

1) the orchestration port operation name
2) the BTS.SOAPAction property
3) the SOAP.MethodName property

Hope this rectifies the problem you are seeing.

Unknown said...

Hello,

I have several sharepoint sites, with different List. I build a biztalk application to update elements of differents lists.

Into a same web site, I use a static port. Everything works perfect. I do not want to create one port for One sharepoint site, I try to use a dynamic port.

To do this, I build a proxy class with the /_vti_bin/lists.asmx?wsdl and I publish it to the GAC. But, I can not have any result. I allways have :

Error Description: Failed to load " biztalkproxy.ListsSoap, biztalkproxy, Version=1.0.0.0, Culture=neutral, PublicKeyToken=27edce3cd8470e0f" type.
Please verify the fully-qualified type name is valid.
Details: "System.TypeLoadException: Could not load type 'biztalkproxy.ListsSoap' from assembly 'biztalkproxy, Version=1.0.0.0, Culture=neutral, PublicKeyToken=27edce3cd8470e0f'.".
The type must derive from System.Web.Services.Protocols.SoapHttpClientProtocol.
The type must have the attribute System.Web.Services.WebServiceBindingAttribute.

Thank you for you help

Mark said...

Hi brice,

From the message you are getting it is clear that the proxy class cannot be loaded. In such cases I would recommend:

a) double check that the assembly containing the proxy is in the GAC.
b) ensure that the SOAP.AssemblyName is correctly set to the expected fully qualified name including namespace.
c) verify that the proxy has public scope and derives from the System.Web.Services.Protocols.SoapHttpClientProtocol class

If all this checks out, you can use tools such as Reflector and SysInternals Process Monitor to check where the assembly is being loaded from and what is in it. It may be you have an old version that is getting picked up somehow.

Hope this helps
Regards
Mark

Unknown said...

Hi Mark,

How can I check those points ?

Thanks,

Brice

Mark said...

Hi Brice,

To check if an assembly is in the GAC, open Windows Explorer and navigate to C:\Windows\Assembly. Check that your assembly (with the correct name, version, culture, public key and architecture) is present.

I would recommend you download Reflector (http://www.red-gate.com/products/reflector/) a free tool which allows you to look inside an assembly. You can use this to check (and copy/paste) the fully qualified name and the scope/base class of the proxy.

Process Monitor (or even the older FileMon) is available from SysInternals (http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx) and can be used to see where an assembly is loaded from. It can also identify failed attempts to load an assembly.

Good luck!

Mark

Unknown said...

Hi Mark,

After several check, my proxy dll is not in c:\windows\assembly. But, it is inside the GAC. When I do a gacutil /l dllproxyname, I find it.

Do you think it is the reason of my errors ?

Thanks,

Brice

Mark said...

You could try running gacutil /u assemblyname to remove it and then add the assembly to the GAC by dragging and dropping it into C:\Windows\Assembly in Windows Explorer.

Unknown said...

I try that, but no dll in the assembly folder !

Unknown said...

For the assembly location, the trouble was the .net version. I compiled it with .net v4, and it was put into an other location. I rebuild it in .net v3.5 and now it is in the correct place.

The new error is
The given assembly name or codebase was invalid

Unknown said...

Now the connexion is good. I have the correct assembly name. But I have an other mistake :
Failed to retrieve the message part for parameter xxx

This parameter name is correctly define into my XML schema. Do you have an idea

Mark said...

Is your XML schema included in the BizTalk project or one referenced by it?