Calling web services diretcly from orchestration

I worked with a customer that made heavy use of synchronous web services and BizTalk. They had receive ports that accepted web service calls, and these ports were bound to orchestrations that contained the logic. The orchestrations aslo called back-end web services. The problem with this approach is that BizTalk is not a very good fit with low-latency requirements. A certain overhead is added to each web service call, which is routed through the message box. So we decided to call a couple of heavily used web services directly from orchestration using a .net helper class. The problem was now how to write this helper. I didn’t want to generate a typed proxy the usual way (using add web service reference or using svcutil.exe), because the BizTalk solution already contained the request and response schemas (the development was using contract first) and messages of these schemas. If we had used a typed proxy, the XML message had been turned into .net objects, which had been serialized to XML again to send the request, and vice versa for the respons. And if the schema (contract) is changed, the proxy must be regenerated.
The solution was to create a client that inherits from ClientBase<IRequestChannel>, and to use the Message class to create the request. Here is the complete code:
    public class ServiceProxy : ClientBase<IRequestChannel>
        /// <summary>
        /// Calls an external web service using WCF.
        /// </summary>
        /// <param name="port">"Port" name. This is used to lookup endpoint address and action in ESSO.</param>
        /// <param name="xlangMessage">The request message to send.</param>
        /// <returns>A message consisting of the response, if successful. If a fault is returned from the service, an exception is thrown.</returns>
        public static XmlDocument Call(string port, XLANGMessage xlangMessage)
                // To use SSOConfigHelper, you must create one or more SSO application and add properties to it.
                // 1. Use SSOManage to create the application:
                // "C:Program FilesCommon FilesEnterprise Single Sign-Onssomanage.exe" -deleteapp MyApp
                // "C:Program FilesCommon FilesEnterprise Single Sign-Onssomanage.exe" -createapps "MyApp.xml"
                // 2. Set property values:
                // "C:Program FilesMicrosoft BizTalk Server 2006SDKScenariosCommonSSOApplicationConfigbinBTSScnSSOApplicationConfig.exe" -set MyApp ConfigProperties MyPortAddress http://aServer/MyService.svc
                // "C:Program FilesMicrosoft BizTalk Server 2006SDKScenariosCommonSSOApplicationConfigbinBTSScnSSOApplicationConfig.exe" -set MyApp ConfigProperties MyPortAction MyAction

                BasicHttpBinding binding = new BasicHttpBinding();
                binding.MaxReceivedMessageSize = 5 * 1024 * 1024; // 5 MB
                // Check if a proxy address is configured.
                string proxyAddress = SSOConfigHelper.Read("ProxyAddress");
                if (proxyAddress != null && proxyAddress != "")
                    binding.UseDefaultWebProxy = false;
                    binding.ProxyAddress = new System.Uri(proxyAddress);
                string addressConfig = port + "Address";
                string actionConfig = port + "Action";
                string endpointAddress = SSOConfigHelper.Read(addressConfig);
                string action = SSOConfigHelper.Read(actionConfig);
                TraceLog.Write(string.Format("Configuration read from SSO: {0}='{1}', {2}='{3}'", addressConfig, endpointAddress, actionConfig, action), "", false, typeof(ServiceProxy).FullName, "Call");
                if (endpointAddress != null && action != null)
                    ServiceProxy client = new ServiceProxy(binding, new EndpointAddress(endpointAddress));
                    return client.CallInternal(action, xlangMessage);
                    throw new Exception(string.Format("Invalid SSO configuration: {0}={1}, {2}={3}", addressConfig, endpointAddress, actionConfig, action));
            catch (MyException)
                // Just rethrow, since this exception has already been logged.
            catch (Exception ex)
                ErrorLog.Write(ex, "", "", typeof(ServiceProxy).FullName, "Call");
                // Always call dispose, because XLANGMessages are reference counted.
                // (,guid,591a086d-59b7-4140-a327-a87091ec8330.aspx)

        private ServiceProxy(Binding binding, EndpointAddress endpointAddress)
            : base(binding, endpointAddress)

        private XmlDocument CallInternal(string action, XLANGMessage xlangMessage)
            TraceLog.Write("Request", "", "", false, GetType().FullName, "CallInternal");
            XmlReader reader = (XmlReader)xlangMessage[0].RetrieveAs(typeof(XmlReader));
            Message req = Message.CreateMessage(MessageVersion.Soap11, action, reader);
            Message reply = Channel.Request(req);
            XmlDocument result = new XmlDocument();
            TraceLog.Write("Response", result.InnerXml, "", true, GetType().FullName, "CallInternal");
            bool ok = !reply.IsFault; 
            if (ok)
                return result;
                throw ContstructException(result);


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.