|
What if you like SOAP and don't like web references?
@08:48:14 PM
This falls into the category "interesting, sort of". I seem to have fielded a lot of questions and comments on BizTalk's Web services support of late. So, in this mini series, I'm going to answer them. First up is debunking (what is 'bunking' anyway?) myths about the SOAP adapter. Namely that it only works with Web references and can't do dynamic sends - hogwash.
The typical use of the SOAP adapter is to wire up a Web service call from an orchestration. This is done by adding a Web reference to the project by pointing at the service's WSDL in question. That's all good, but it is also rather limited. Essentially, BizTalk is just leveraging the asmx plumbing which creates a proxy class (which is added to the orchestration assembly) in the same way as it would be if you were doing it in a forms or console app. Once you have the Web reference, you can reference it in the orchestration designer when creating a send(/receive) port. BizTalk will create the appropriate multipart message types based on the WSDL to allow you to use message instances to communicate with the service (something like HelloWorld_request/HelloWorld_response).
All well and good. But remember I said this was a bit limited? The first limitation is that what if you didn't want to use web references, but still wanted to use the SOAP adapter? We'll get to why in a minute. If you try this, and just use a standard port, configuring the bindings in the Explorer (as you'll no longer get a BizTalk generated port - that's an added bonus), it won't work. You'll get the following error:
"Failed to load "" type. Please verify the fully-qualified type name is valid. Details: "". The type must derive from System.Web.Services.Protocols.SoapHttpClientProtocol. The type must have the attribute System.Web.Services.WebServiceBindingAttribute. ".
hmmm. Something is obviously missing. This is actually a pretty easy fix. What is happening when you use a proper Web reference port is that the following properties are promoted on to the context before the message is written to the message box as it is sent out of the orchestration:
SOAP.MethodName SOAP.AssemblyName
There are others, but these are the important ones to us. The method name is the of the operation. In this case it is the same as the [WebMethod]. Again, there's more to this, but it's not important to us here. Thus, if in the orchestration we set this property prior to the send shape, it will work. Well, not quite, we also need to set the SOAP.AssemblyName too. This is the name of the proxy assembly and type (i.e. the proxy class) that was generated for us by adding the Web reference. But we don't have one of those now. Oh.
So, let's look at why we want to do this. One of the problems with the asmx approach and BizTalk is that it makes for a very rigid coupling. It is hard to change (or extend) the underlying schema without having to update the Web reference. If we removed this (as we have) then we can take control over what the proxy looks like and handle the versioning ourselves in a more controlled, considered fashion. So to immediately solve this issue all we need to do is run wsdl.exe giving it the URL of the Web service's WSDL. If we compile it up and GAC it we can then set the SOAP.AssemblyName value and the call will work. Voila, no Web refs.
Now for the second case. Dynamic ports. What if we had the requirement that we wanted to send our message over MQ, FTP or SOAP? We just can't make our minds up until run time. This again is actually fairly straightforward but there's an interesting wrinkle.
If we change the port to be dynamic then we have to set another property in our orch: the port's Microsoft.XLANGs.BaseTypes.Address property. This has to include the URI scheme (that's the first bit before the ':'). So, for the SOAP adapter, this will be SOAP://. THIS HAS TO BE DONE in order for the engine to send the message to the right adapter. Each adapter registers for particular schemes so the engine knows what to do. Unfortunately what then happens before the message is published is that this is copied to the BTS.OutboundTransportLocation property. When the SOAP adapter receives the message, it throws up, with the following error in the event log:
NotSupportedException: The URI prefix is not recognized.
Which is true, because the OutboundTransportLocation is using SOAP:// not HTTP://. The only way I've found to resolve this so far is to set it back in the send pipeline. Simply by replacing the scheme SOAP with HTTP fixes this and we can use dynamic ports no problem. Obviously other adapters handle this, but then the don't have the same issue - I guess the SOAP adapter suffers because of the engine URI mapping/transport URI mapping difference (i.e. it's SOAP *over* HTTP).
One of the major scenarios with these two cases is that what if there is no orchestration? Then you *cant* have a Web reference. Let's say you just want to do fast routing from say MSMQ to a Web service. All you need to do is set the properties in the pipeline and you're good to go.
The key with all this is remembering that there is no magic. Everything has to go through the message box. That means that if an adapter needs some funky context properties set they better be set before the message makes it into the message box. That way if the adapter is running on a separate host (and machine) it knows what's going on. I should note that all of this applies equally for BTS 2004 and 2006.
My next post in this (very short) series is going to be about doing SOAP without the SOAP adapter.
--
6
comments:
View
-
Post your own comment
|
Posted by @
02:59 PM, November 30
Could address how you get the message type to send? What if the web service has a complex type as a parameter? Without a web reference how do you know what to send?
|
|
|
Posted by @
05:06 PM, July 02
Great Article! Do you have examples of how to do what is contained in this article and can you email them to me? It would really help me resolve a problem that I am having.
Thanks
|
|
|
Posted by @
05:06 PM, July 02
Great Article! Do you have examples of how to do what is contained in this article? It would really help me resolve a problem that I am having.
Thanks
|
|
|
Posted by @
04:23 PM, January 31
How do you set the SOAP.AssemblyName and SOAP.MethodName properties? I have a port named Port_2 and sets the following in the expression shape:
Port_2(Microsoft.XLANGs.BaseTypes.Address) = "soap://localhost/MyWebservice/ProcessService.asmx"; Port_2(SOAP.MethodName) = "ProcessMsg"; Port_2(SOAP.AssemblyName) = "WSProxy.Service1";
Where ProcessMsg is the name of the method I want to call and WSProxy the assembly name of the proxy and Service1 the name of tyhe proxy class.
I get the following error in the orchestration when trying to deploy:
SOAP.MethodName must be a port property of port Port_2 SOAP.AssemblyName must be a port property of port Port_2
How shall this be done?
|
|
|
Posted by @
02:35 PM, January 22
Great posting about dynamic HTTP send ports. You stated that you had to correct the problem with the SOAP adapter in the pipeline. Can you email what you did. I would like to try this out. Thanks
|
|
|
Posted by @
09:55 PM, November 29
This is a great post on SOAP without web references. I have been trying to develop a prototype. I tried to provide method name and assemply name in BizTalk Admin Console. However, I still encountered the above error. If these parameters can be assigned dynamically in orchestration using expressions, then what do I specify for web service url when beinding a physical port? Appreciate yoru help.
|
|