A Generic WCF Service for BizTalk

I've been playing lately with a few WCF features as well as with the WCF Adapter in BizTalk Server 2006 R2. As part of that I had a need to set up a receive location using one of the WCF HTTP-based bindings.

Normally, this isn't a big deal; you can easily use the BizTalk WCF Service Publishing Wizard to create the receive location and IIS service application for an orchestration or from a set of BizTalk schemas.

However, this time I did not want to go that way. What I really wanted was just a simple receive location that would simply receive messages and submit them over to BizTalk without tying the receive location to a specific service definition. As far as I could see, the Service Publishing Wizard didn't really have an option for this, but I was confident it could be made to work.

To be honest, I did have a set of schemas I wanted to work with and even a bunch of predefined WSDL files. However, for many reasons (including the sheer complexity of the schemas and WSDL files involved) I didn't want to get that involved in my initial BizTalk message receiver and processor.

Fortunately, turns out that doing what I wanted was fairly easy stuff. In fact, it was so easy that was able to leverage an existing service I had previously created. I basically just copied the .SVC file (and renamed it) alongside the web.config file. I explicitly ignored all the stuff that normally gets generated under the App_Data directory.

I then manually created a matching receive location in BizTalk using the WCF-BasicHTTP adapter, and this worked right away perfectly for my needs!

Just in case you've never looked at what a WCF HTTP receive location generated files look like, it's actually fairly simple stuff. The SVC file contains just a single directive as expected:

<%@ ServiceHost Language="c#" Factory="Microsoft.BizTalk.Adapter.Wcf.Runtime.BasicHttpWebServiceHostFactory, Microsoft.BizTalk.Adapter.Wcf.Runtime, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>

You can see here that it references the WCF-BasicHTTP adapter. If you wanted to use, say, the WCF-WSHttp Adapter, then you'd use Microsoft.BizTalk.Adapter.Wcf.Runtime.WSHttpWebServiceHostFactory class instead.

The config file is pretty much the default config file generated by the Service Publishing Wizard as well:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
   <configSections>
      <section 
         name="bizTalkSettings" 
         type="Microsoft.BizTalk.Adapter.Wcf.Runtime.BizTalkConfigurationSection,
            Microsoft.BizTalk.Adapter.Wcf.Runtime, Version=3.0.1.0,
            Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
   </configSections>
   <bizTalkSettings>
      <mexServiceHostFactory debug="false">
         <receiveLocationMappings>
            <!--add markupFileName="*.svc"
               receiveLocationName="?"
               publicBaseAddress="protocol://host[:port]" /-->
         </receiveLocationMappings>
      </mexServiceHostFactory>
      <webServiceHostFactory debug="false" />
      <isolatedReceiver disable="false" />
      <btsWsdlExporter disable="false" />
   </bizTalkSettings>
   <appSettings />
   <connectionStrings />
   <system.web>
      <compilation defaultLanguage="c#" debug="false">
         <assemblies>
            <add assembly="mscorlib, version=2.0.0.0, culture=neutral,
          60;     publickeytoken=b77a5c561934e089" />
            <add assembly="Microsoft.BizTalk.Adapter.Wcf.Common, Version=3.0.1.0,
               Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
            <add assembly="Microsoft.BizTalk.Adapter.Wcf.Runtime, Version=3.0.1.0,
               Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
         </assemblies>
      </compilation>
      <authentication mode="Windows" />
   </system.web>
   <system.serviceModel>
      <behaviors>
         <serviceBehaviors>
            <behavior name="ServiceBehaviorConfiguration">
               <serviceDebug 
                  httpHelpPageEnabled="true" 
                  httpsHelpPageEnabled="false" 
                  includeExceptionDetailInFaults="false" />
               <serviceMetadata 
                  httpGetEnabled="false" 
                  httpsGetEnabled="false" />
            </behavior>
         </serviceBehaviors>
      </behaviors>
      <services>
         <service 
            name="Microsoft.BizTalk.Adapter.Wcf.Runtime.BizTalkServiceInstance" 
            behaviorConfiguration="ServiceBehaviorConfiguration">
            <!--<endpoint
               name="HttpMexEndpoint"
               address="mex"
               binding="mexHttpBinding"
               bindingConfiguration=""
               contract="IMetadataExchange" />-->
            <!--<endpoint
               name="HttpsMexEndpoint"
               address="mex"
               binding="mexHttpsBinding"
               bindingConfiguration=""
               contract="IMetadataExchange" />-->
         </service>
      </services>
   </system.serviceModel>
</configuration>

It's extremely nice to see how the WCF adapter in BizTalk leverages a bunch of my favorite features in WCF to make this a lot simpler (compared to, say, the whole bunch of code that neede d to be generated for the original SOAP adapter).

I should mention though, that part of what made it so easy was that my needs were pretty simple: I wanted a simple two-way (request/response) port, and needed no metadata (WSDL) publishing at all (as I said, I already had working WSDL files I could provide to consumers of the service).  Making it one-way wouldn't have been a problem though; as the WCF adapter handles it very gracefully as well.

Technorati tags: ,

10 comments

  1. With regards to WSDL, you can expose it by adding and configuring the serviceMetadata section in the BT receive location’s Behavior tab and then setting the externalMetadataLocation property. The only minor challenge with that is any changes that you make to your messages (i.e. schemas) need to be reflected in the WSDL. Still, it is a small price to pay for simplicity. :)
    We’ve found that replicating a generic service and exposing a WSDL document is so much more efficient and less frustrating than using the BT WCF publishing wizard.

  2. Joel: Yes, I’m aware of externalMetadataLocation, but totally forgot about it; thanks for bringing it up.
    Actually, I don’t have to worry in this case about the WSDL and the schemas getting out of sync because both are part of a standard control by a third party. So this is really kind of the scenario where this shines!

  3. HI ,
    Can you please send me the code for above snippet or any webcast related to the above scenario.
    Thanks
    reddy

  4. @reddy: There’s really no extra code; those two snippets are really all it takes to enable a WCF receive location for BizTalk. Is there anything specifically you’re looking for?

  5. Hi Tomas,

    I”m trying to migrate from some scenario which works for ASMX – to WCF:

    Im publishing two schemas, one request and the other – response, as a web service. After I run the wizard, Im changing the generated code in *.cs file of web application – to responde immidiately, and not to wait while some BTS application will respond for the request. Since after running WCF publishiung wizard there’s no any code, just binding and seralization schema, how can I interfere into my request-response flow? Have any idea?

  6. @Yonathan: I don’t think you’ll be able to do exactly the same. To be honest, the way you’re currently doing it is a bit of a hack. The “right” (if not necessarily the most efficient way to do it) would be to have the called orchestration send a response message right away and then continue on it’s merry way.

    I’m curious, though as to what kind of response you could’ve generated on your asmx hack that wasn’t a default “done” response (and if so, why you didn’t just go with a one-way service in the first place)

  7. Hi Tomas,

    Thanx for reply.

    What you are saying about using orchestration as a responser – it is truly the correct way, but it is still another piece of code to maintain.
    The “hack” is just returning “Succeess” or “Failure with description” immidiately after the message was put (or not ☺ ) to the message box.
    Anyway, finally we did use one-way WCF with custom binding.

    Though, now there’s another issue with sertificate definitions – it looks like Receive Location’s definition doesn’t keep definition for extension called “context”. After I choose to use Sign only incoming message – and if I export receive location *.config, the line called stays empty. Even more than that: when I add to *.config desired definition manually – and import that configuration, and immidiately export configuration back – the definition is cleared.

    Here http://social.msdn.microsoft.com/forums/en-US/biztalkr2adapters/thread/46e9345c-cb50-44ce-9084-807b9b3c1c58/ I see the exact problem, with slight difference – that instead of generated client – I have published schemas as WCF – therefore I have no code to add attribute to.

    Possibly the solution to my problem will be writing some little WCF-Proxy, which will “pass” to Biztalk non-encrypted message, and the client from other side will contact with that proxy, and not actually Biztalk itself.

  8. i want to receive the untyped message (i.e. System.Xml.XmlDocument) from WCF service. what needs to be done in this case.
    your help will be higly appreciated

  9. @Dolly: You wouldn’t need to change anything in the .SVC or config file at all… it would really be a matter of setting up your orchestration to take an arbitrary document as input (XmlDocument) and making sure it is bound to the WCF receive port.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>