The last post showed an itinerary for sending a one-way message, to be transformed and stored using a FILE adapter. For two-way messaging, the process is very similar. The "Routing" service step needs to specify the location of the endpoint (the service), the target namespace and the action being invoked (the method). Again, using STATIC resolution here (I'll look at UDDI in a separate post), ESB Guidance provides a WCF resolver which can be used to invoke a WCF service:
<Resolvers serviceId="Microsoft.Practices.ESB.Services.Routing0">
<![CDATA[
STATIC:\\TransportType=WCF-BasicHttp;
TransportLocation=http://localhost/Demo.Integration.ClientService.WCF/ClientService.svc;
TargetNamespace=http://tempuri.org;
Action=http://tempuri.org/ClientServices/GetClientById;
]]>
</Resolvers>
(Note, for STATIC resolvers you only need to specify the parts of the connection string the resolver will actually use).
The same WCF resolver can be used for a standard SOAP Web service using the BasicHttp policy:
<Resolvers serviceId="Microsoft.Practices.ESB.Services.Routing1">
<![CDATA[
STATIC:\\TransportType=WCF-BasicHttp;
TransportLocation=http://localhost/ESBSimpleSamples/EncodingService.asmx;
Action=http://ESBSimpleSamples.BizTalk.Schemas/1.0/GetASCIICode;
TargetNamespace=http://ESBSimpleSamples.BizTalk.Schemas/1.0/;
]]>
</Resolvers>
The final step of the itinerary which ends up submitting a message to the message box to be picked up by a dynamic port is still needed. This effectively is the publishing step, where the port receives a fully configured message and submits it in passthrough fashion. The sample Global Bank BizTalk app comes with a configured dynamic two-way port to use for this, which we'll use so the complete itinerary looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Itinerary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" uuid="" beginTime="" completeTime="" state="Pending" isRequestResponse="false" xmlns="http://schemas.microsoft.biztalk.practices.esb.com/itinerary">
<ServiceInstance uuid="" name="Microsoft.Practices.ESB.Services.Transform" type="Messaging" state="Pending" position="0" isRequestResponse="false" xmlns="" />
<Services xmlns="">
<Service uuid="" beginTime="" completeTime="" name="Microsoft.Practices.ESB.Services.Transform" type="Messaging" state="Pending" isRequestResponse="false" position="0" serviceInstanceId="" />
</Services>
<Services xmlns="">
<Service uuid="" beginTime="" completeTime="" name="Microsoft.Practices.ESB.Services.Routing" type="Messaging" state="Pending" isRequestResponse="false" position="1" serviceInstanceId="" />
</Services>
<Services xmlns="">
<Service uuid="" beginTime="" completeTime="" name="DynamicResolutionSolicitResp" type="Messaging" state="Pending" isRequestResponse="false" position="2" serviceInstanceId="" />
</Services>
<ResolverGroups xmlns="">
<Resolvers serviceId="Microsoft.Practices.ESB.Services.Transform0"><![CDATA[STATIC:\\TransformType=ESBSimpleSamples.BizTalk.Maps.Parameters_GetASCIICodeRequest,ESBSimpleSamples.BizTalk, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a9e52fd7be56a687;]]></Resolvers>
<Resolvers serviceId="Microsoft.Practices.ESB.Services.Routing1"><![CDATA[STATIC:\\TransportType=WCF-BasicHttp;TransportLocation=http://localhost/ESBSimpleSamples/EncodingService.asmx;Action=http://ESBSimpleSamples.BizTalk.Schemas/1.0/GetASCIICode;TargetNamespace=http://ESBSimpleSamples.BizTalk.Schemas/1.0/;]]></Resolvers>
<Resolvers serviceId="DynamicResolutionSolicitResp2" />
</ResolverGroups>
</Itinerary>
Note there's also a mapping step. In the two-way message, the content of the message body is what makes it to the endpoint. If the message does not match the expected schema or target namespace, it will either get stuck in BizTalk or the endpoint will receive null parameters. Similarly for the return message, the body will contain the response from the endpoint, so BizTalk needs to know about that schema too.
For SOAP and WCF messages, the request and response schemas wrap the interface of the service, so this service:
byte[] GetASCIICode(string Character)
has schemas for request and response like this:
<xs:schema targetNamespace="http://ESBSimpleSamples.BizTalk.Schemas/1.0/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="GetASCIICode">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="Character" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
<xs:schema targetNamespace="http://ESBSimpleSamples.BizTalk.Schemas/1.0/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="GetASCIICodeResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="GetASCIICodeResult" type="xs:base64Binary" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
WebServiceStudio is useful here, as you can point it to a .svc or .asmx endpoint, fire a Get request and it will show you the full request and response messages and schemas.
The service schemas will need to be published in BizTalk if you want to use ESB Guidance in a typesafe manner. The alternative is to turn off message validation at the receive locations and send ports(set Validate to false and AllowUnrecognizedMessage to true on the itinerary pipeline components). This has the advantage of needing even fewer BizTalk artifacts – with message type validation off, itineraries which only have message routing steps will not need any BizTalk artifacts at all.
The disadvantage is that you lose your typesafety, and tracking down any issues will be much more difficult. It also means that the service consumer needs to format the request and response message bodies itself, so it needs to understand the request and response schemas. This tightly couples the consumer to the service, so you lose the ability to change services on the fly, with no consumer changes, unless the schemas are identical.
It's neater to have that mapping in BizTalk – for example to have a generic request schema which contains name-value pairs for the parameters to be sent to the service. In the itinerary this is mapped to the service's request schema, and the response is mapped from the service's schema to the client's expected schema. The next post will use this approach, and demonstrate constructing itineraries in code.