Calling Web Service from BizTalk 2006 in a Messaging only Scenario (aka Content based Routing)

Posted at: 1/31/2007 at 6:45 PM by saravana

In this article I'll explain how you can call a Web Service which requires multiple arguments using a Custom pipeline and a custom pipeline component in a messaging-only scenario without using any Orchestration.

 

Normally, when there is a requirement to call a web service from BizTalk, people tend to take the easy route of calling it via an Orchestration. When we do a web reference inside the orchestration, Orchestration does quite a lot of work for us. It creates all the required schemas, it creates all the required multipart messages, which will be passed to the web service as argument. It makes our life easier. But I guess like me, some of you out there might need to call the web service without using Orchestration. As shown in the above figure. I've one request-response HTTP receive port, and one Solicit response SOAP send port, through this I'm going to call a web service, which expects multiple argument (including one complex type) and return the result back to the caller (HTTP Response). Here are the steps: The attached sample file contains all the required file, I'm just going to explain the key factors in this article.

1. Web Service Definition:

[WebMethod]
public Person GetPersonInfo(Person person, string firstName, string secondName) {
//Some processing
return person;
}

2. Create a general custom pipeline component to construct the multipart message required for the Web Service call 

At run time SOAP Adapter in the send port will map the Biztalk multipart IBaseMessage to the Web Service argument based on the partName of IBaseMessage and argument names of the Webservice. The key factor is how we are going to construct the multipart message in the format required by the SOAP Adapter to make the WebService call. In my previous article I explained how you can easily generate the required IBaseMessage with help of MIME message and MIME Decoder component. But when it comes to reality we don't want to get into another data format like MIME. So, in this article we are going to create custom pipeline component which will construct the correct IBaseMessage required by the SOAP adapter based on the input message and some pipeline design time properties (for more detail on design time properties see my white paper ) .

The custom pipeline component we are going to use has 2 design time properties FirstName and SecondName, which will be passed as parameters to the web service (See webservice definition from Step 1). We'll pass the first webservice argument "Person" as the incoming message via HTTP receive port. The figure below show the custom design time properties configuration window within Biztalk Admin console. 

The code below is the snippet from the custom pipeline component (two important methods Execute and CreateMessage). The Execute method below without the first line of code will be equivalent to a PassThru pipeline component with default Biztalk IBaseMessage. 

#############################################################

public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute(IPipelineContext pc, IBaseMessage inmsg)
{
IBaseMessage msg = CreateMessage(inmsg.BodyPart.GetOriginalDataStream(), pc.GetMessageFactory(),inmsg.Context);
return msg;
}

#############################################################

IBaseMessage CreateMessage(Stream s, IBaseMessageFactory msgFactory, IBaseMessageContext context)
{
IBaseMessage msg = msgFactory.CreateMessage();
IBaseMessagePart part = msgFactory.CreateMessagePart();
part.Data = s;

msg.AddPart("Person", part, true);
msg.Context = context;

//1st Part
IBaseMessagePart partFirstName = msgFactory.CreateMessagePart();
byte[] firstPart = System.Text.Encoding.UTF8.GetBytes(string.Format("<string>{0}</string>", _firstName));
partFirstName.Data = new MemoryStream(firstPart);
partFirstName.Charset = "utf-8"
partFirstName.ContentType = "text/xml"
msg.AddPart("firstName", partFirstName, false);

//2nd Part
IBaseMessagePart partSecondName = msgFactory.CreateMessagePart();
byte[] secondPart = System.Text.Encoding.UTF8.GetBytes(string.Format("<string>{0}</string>", _secondName));
partSecondName.Data = new MemoryStream(secondPart);
partSecondName.Charset = "utf-8"
partSecondName.ContentType = "text/xml"
msg.AddPart("secondName", partSecondName, false);
return msg;
}

#############################################################

Our user defined function CreateMessage will create the required BizTalk IBaseMessage as shown in the below figure

In the above code snippet, the important things to note are highlighted in RED. The incoming message ("Person") will go as the first part (BodyPart) of the IBaseMessage with the name "Person", and then we added two more addional parts "firstName" and "secondName" to the IBaseMessage with correct partNames inline with the web service arguments. The other important thing to note is how the basic data types gets serialized. In our example we got "<string>{0}</string>" as value for firstName and secondName, because they are of type string. If for example you got int as your argument then you need to create the part in the format <int>5</int>.

NOTE: See the web service signature defined in Step 1 for comparison

3. Create a Custom Receive Pipeline using the custom pipeline component

Create a new Biztalk Receive Pipeline and place the custom pipeline component we created in the "Decode" stage of the pipeline.

4. Configure the ports

As shown in our design diagram at the beginning we need 2 ports to send and receive the message, the attached sample file got a binding file, this section is just for explanation, doesn't explain in detail how to configure the ports. Make sure the URL are correct, both on Receive and Send side after importing the binding. You need to configure IIS as well to receive messages via HTTP, follow the link to configure IIS for HTTP receive  http://msdn2.microsoft.com/en-us/library/aa559072.aspx.

Two-Way HTTP Receive Port:

Solicit-Response SOAP Send Port:

We used the .NET Proxy class on our SOAP port to make the call.

Filter Condition on the Send Port

5. Post a Message.

I used WFetch to post the message to BizTalk. You can see on the result pane the request message is posted and you got the response back from the web service synchronously on a two way connection.

Troubleshooting:

Some of the common exceptions you'll see while calling a webservice via SOAP adapter is shown below (from HAT and eventviewer)

1. "Failed to retrieve the message part for parameter "firstName". "

2. "Failed to serialize the message part "firstName" into the type "String" using namespace "". Please ensure that the message part stream is created properly."

The reason for the first error message is due to wrongly named IBaseMessage partName. Read Section 2 carefully to overcome this error.

The reason for the second error message is mainly due to some problem with serializing the IBaseMessage parts to the correct web service arguments. Best approach to overcome this error will be to build a .net console/windows application, add a web reference to the webservice and try to serialize each argument to the corresponding type. For example for this example you can try the following

FileStream fs = new FileStream(@"C:\Documents and Settings\SaravanaK\Desktop\FailedMessages\_Person.out",FileMode.Open,FileAccess.Read);
XmlSerializer serialise = new XmlSerializer(typeof(LH.WebReference.Person));
LH.WebReference.Person per = (LH.WebReference.Person)serialise.Deserialize(fs);
fs.Close();

fs = new FileStream(@"C:\Documents and Settings\SaravanaK\Desktop\FailedMessages\_secondName.out",FileMode.Open,FileAccess.Read);
serialise = new XmlSerializer(typeof(string));
string s2 = (string)serialise.Deserialize(fs);
fs.Close();

The files "_Person.out" and "_secondName.out" are saved from HAT tool. See the exception detail and fix the issue, it will be some namespace issue or data issue.

DOWNLOAD SAMPLE

Read the readme.txt file inside to configure it. Will take approximately 5-20 minutes based on your BizTalk knowledge level.

Related Post

In one of my previous post I explained how to call a web service that has more than one argument using a MIME message in a Content based routing(messaging only) scenario.

Nandri!

Saravana Kumar

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags: | |  Categories: BizTalk 2006 | BizTalk General
Actions: Email this article Email | Kick it! | DZone it! | Save to del.icio.us | Technorati Links
Post Information: Permanent LinkPermalink | CommentsComments(14) | Comments RSS

Comments

Wednesday, February 14, 2007 7:12 PM
Sundar Rajan
Hi Saravana,

Very good article. I just have one question related to this topic. I am trying to call an XML Web Service (created using VS.NET 2003) within Biztalk 2006 orchestration. When I add the Web Reference the Reference.xsd file is not created while all other files Reference.map, Reference.odx, wsdl and disco files are created. Without Reference.xsd I cannot do any mapping. How to do this.
Wednesday, February 14, 2007 7:13 PM
Sundar Rajan
Monday, February 19, 2007 5:30 PM
Saravana Kumar
Hello Sundar, I hope you would have solved this problem by now. Adding a Web Reference to a Web Service is quite straight forward process within you orchestration project. As soon as you reference the webservice, you should see all the xsd files named something like lc.xsd, lc0.xsd, lc1.xsd etc. In addition to that you'll see Reference.map under which you'll see all the Reference.* files.

If you still have the issue let me know. With you explanation I'm not able to identify your problem.
Thursday, March 01, 2007 5:08 AM
Adriaan
Adriaan
Hi Sundar. thanks for this good post.

I have an orchestration that consumes an Authentication WebService. The Websvc returns a sessionID GUID as a custom property inside the SOAP header response. I need to promote this to property so I can correlate when submitting to another WebSvc ,passing the sessionid in the other SOAP header.

Do you have any idea where I should start ? Regards Adriaan
Wednesday, March 07, 2007 3:12 PM
Rajeev
Hi It is really a great post bt I need some help. I have created a web service in Dot Net 2005 that has a web methid that takes one input and it is a customer id that is a string and returns a dataset that contains customer name address etc.

I dont want to use any orchestration I want to achiev this by pure messaging so i created a File adapter to receive an xml file with one parameter as Customerid and a string value and I have a soap adapter that calls the web method but when I submit the file it gives me an error cannot serialize to tye string.

Please can you guide me in this
Wednesday, March 07, 2007 6:33 PM
Saravana Kumar
Hello Rajeev, thats one of the common errors you see when you try to call a Web service via soap adapter on a messaging only scenario. Did you work out the sample of this post, the example i used got a string parameter as well. When you are creating the IBaseMessage inside your pipeline, you need to make sure the part content for the string is populated like this
string.Format("<string>{0}</string>", _firstName).

Nandri!
Saravana
Friday, March 09, 2007 7:24 PM
Rajeev
HI thanks for your response I tried your approach and it worked for me but I guess there is a limitation here we need to specify the firstname value in the pipeline properties but if I want to pick it from the xml schema what is the approach that I should take
Friday, March 30, 2007 3:45 PM
Gar
Gar
Hi Saravana,
Great article I'm wondering how you approach calling a web service that has customer SOAP headers e.g. a UserCredentials Header with a userName and password. Is it simply case of creating another messagePart with the Credentials or does it need to get added in anywhere else.

Regards
Gar
Wednesday, June 13, 2007 3:24 PM
Saravana
Hello Saravana
Can i call webservice which has only request method since we dont want to get response from Web services.
Thanks
Saravana ramkumar
Friday, October 12, 2007 6:25 PM
praveen
hi saravana,
thsi is kumar, i read the article its awesome, i got a question what happens when we call web service from orchestration -- will the meaassage be serliazed everytime you call the web service???? please do mail me if you anwser at pmognti@gmail.com
Friday, October 26, 2007 5:25 PM
Scott Brown
I wrote a small article about adding SOAP headers that addresses some of the comments on this post.

biztalkspeaks.blogspot.com/.../...-in-biztalk.html
Wednesday, November 07, 2007 9:51 AM
Anonymous
Anonymous
Still didn't get what Sundar rajan's resolution was. I created a sample WebService and imported to Biztalk. It doesn't generate the xsd. My service takes a number and returns the square of it. Please post ur comment.. I would follow up
Wednesday, November 07, 2007 9:58 AM
Saravana Kumar
For basic data types like int, double you don't need schemas (XSD). If you have complex custom types like example Person, Employee, Purchase order etc as input/output then xsd will be created. For basic types you can assign them directly within an expression shape.

Regards,
Saravana
Thursday, January 17, 2008 1:35 PM
Hari
Hari
Hi Saravana,

I need to call web service from orchestration.I have only wsdl with me.When i do web reference it fails during compilation.THis is because the generated reference.xsd is not valid.
Please help me to solve this.

Thanks
Hari

Add comment


(Will show your Gravatar icon)  

  Country flag

biuquote
  • Comment
  • Preview
Loading