Skip to the content.

Adapter Binding

Downloads The code samples used in this user guide have been made available in the Be.Stateless.BizTalk.Factory.Samples GitHub repository.

Available Adapters

The namespace Be.Stateless.BizTalk.Dsl.Binding.Adapter provides a rich set of adapter configuration classes —e.g. FileAdapter or WcfSqlAdapter. Every such class furthermore nests either or both an inbound and outbound classes —e.g. FileAdapter.Inbound or WcfSqlAdapter.Outbound— that surface the actual configuration properties of respectively the inbound or outbound adapter.

As detailed in the following table, BizTalk.Factory’s Binding DSL currently supports most of the built-in Microsoft BizTalk Server® adapters:

Adapter Inbound Outbound Deprecated
AzureBlobStorageAdapter  
EventHubAdapter  
FileAdapter  
FtpAdapter  
HttpAdapter  
LogicAppAdapter  
MQSeriesAdapter  
MsmqAdapter  
Office365CalendarAdapter  
Office365ContactAdapter  
Office365EmailAdapter  
Pop3Adapter -
SBMessagingAdapter  
SharePointOnlineAdapter  
SftpAdapter  
SmtpAdapter -
SoapAdapter
SqlAdapter
WcfBasicHttpAdapter  
WcfBasicHttpRelayAdapter  
WcfCustomAdapter  
WcfCustomIsolatedAdapter -  
WcfNetMsmqAdapter  
WcfNetNamedPipeAdapter  
WcfNetTcpAdapter  
WcfNetTcpRelayAdapter
WcfOracleAdapter  
WcfOracleEBusinessAdapter  
WcfSapAdapter  
WcfSiebelAdapter  
WcfSqlAdapter  
WcfWebHttpAdapter  
WcfWSHttpAdapter  
WindowsSharePointServicesAdapter  

Remark The vast majority of the adapter configuration classes provided by Be.Stateless.BizTalk.Dsl.Binding is a delegation API that taps right into the System.ServiceModel.Configuration.StandardBindingElement-derived classes or other Microsoft BizTalk Server® native configuration classes. The developer should therefore feel confident that the configuration bindings produced by BizTalk.Factory’s Binding DSL are truly accurate and faithful to any actual Microsoft BizTalk Server® application bindings that would be produced otherwise.

WCF-based Adapters

WCF-based adapters typically provide many highly customizable features. Since WCF was built as a SOAP stack, these adapters therefore present a configuration API which exposes core SOAP concepts. Among them, we will retain actions, bindings, and behaviors, which are customization areas that deserve further in-depth treatment.

SOAP Action Configuration

Action Mapping

SOAP action configuration is performed through the StaticAction property of the adapter —see the IAdapterConfigOutboundAction interface. Even though the StaticProperty is a simple string, it can either be configured to a single SOAP action literal:

adapter.StaticAction = "http://services.stateless.be/biztalk/factory/mail/1.1/IMailService/SendMessage";

or to a set of mappings from Microsoft BizTalk Server® operations to SOAP action literals:

using Be.Stateless.BizTalk.Adapter.Metadata;

...

adapter.StaticAction = new ActionMapping {
  new ActionMappingOperation(
    "UpdateIntervention",
    "http://Microsoft.LobServices.OracleDB/2007/03/TICKETING/Procedure/UPDATE_INTERVENTION"),
  new ActionMappingOperation(
    "UpdateOperation",
    "http://Microsoft.LobServices.OracleDB/2007/03/TICKETING/Procedure/UPDATE_OPERATION")
};

Remark If you use JetBrains ReSharper you can get the StaticAction quick help as a reminder on how to configure the property, as illustrated here.
Press ctrl+shift+F1 with the caret inside the lexical token for which you want to get the help; in this case, StaticAction.

HTTP Methods and URL Mapping

Though meant to provide RESTful interactions through parameterized URLs and HTTP methods —which somehow are to REST what actions are to SOAP,— the WcfWebHttpAdapter adapter has been built on top of WCF, which is SOAP centric. The WCF stack and configuration model therefore had to be twisted to support the feature set of RESTful interactions and, as a result, idiosyncratic configuration properties such as HttpUrlMapping, VariableMapping, and SuppressMessageBodyForHttpVerbs, surfaced through the configuration API of the WcfWebHttpAdapter.

Transport.Adapter = new WcfWebHttpAdapter.Outbound(
   a => {
      a.Address = new EndpointAddress($"http://{Application.Settings.ApiHost}/api");
      a.HttpHeaders = "Content-Type: application/xml\r\n"
         + "Accept: application/xml";
      a.HttpUrlMapping = new HttpUrlMapping {
         new HttpUrlMappingOperation(
            Processes.Declare.ProcessOrchestrationBinding.RequestApiPort.Operations.GetRequest.Name,
            HttpMethod.Get.Method,
            $"/v1/request/}")
      };
      a.SuppressMessageBodyForHttpVerbs = HttpMethod.Get.Method;
      a.VariableMapping = new VariableMapping {
         new VariablePropertyMapping(QueryParameterProperties.RequestId.Name, QueryParameterProperties.RequestId)
      };
      a.EndpointBehaviors = new BehaviorExtensionElement[] {
         new WebHttpElement { FaultExceptionEnabled = true },
         new WebHttpAuthorizationBehaviorExtension {
            AuthorizationTokenServiceMiddlewareType = typeof(AuthorizationTokenServiceMiddleware),
            AuthorizationTokenServiceUri = $"http://{Application.Settings.ApiHost}/api/token/service"
         },
         new WebHttpErrorInspectionBehaviorExtension()
      };
});

WCF Behavior Configuration

How a WCF-based adapter actually interacts with its counterpart at runtime can be customized thanks to behavior components which will be injected in the communication channel at construction time. Depending on whether the WCF exposes or consumes an endpoint, behavior customization is respectively surfaced through either the ServiceBehaviors or the EndpointBehaviors property. An example of how to configure the EndpointBehaviors has been provided in the previous code excerpt.

Remark Even though every WCF communication channel can be an injection site for custom behavior components, most of the Microsoft BizTalk Server® built-in WCF-based adapters do not allow injecting custom behavior components but come with predefined ones. As a result, the WCF channel per se is not customizable, but the individual behavior components are.

To customize the WCF channel the developer therefore has no other choice than to fall-back on the WcfCustomAdapter or WcfCustomIsolatedAdapter and reconfigure the whole channel according to the binding —HTTP, net.tcp, WSHttp— he whishes to use.

Microsoft BizTalk Server® LOB Adapters such as WcfOracleAdapter,WcfSapAdapter, or WcfSqlAdapter, as well as the WcfWebHttpAdapter are noticeable exceptions that always allow the WCF channel to be customized with behavior components.

WCF Binding Configuration

The WCF binding is the actual transport, such as HTTP, being used to connect to an endpoint —technically a binding can define much more than the transport as explained here. For Microsoft BizTalk Server® WCF-based adapters, the binding configuration is performed through the Binding property of the adapter —see the IAdapterConfigBinding<T> interface. As most of the Microsoft BizTalk Server® adapters assumes a predefined transport, only the WcfCustomAdapter and WcfCustomIsolatedAdapter adapter support binding configuration.

How to configure an inbound WcfCustomAdapter is illustrated in the following code excerpt. Notice that the binding denoting the transport to use, i.e. NetTcpBindingElement, is given as a generic type argument and that is the actual WCF binding element configuration class and not a custom wrapper provided by BizTalk.Factory.

const int tenMegaBytes = 10 * 1024 * 1024;

Transport.Adapter = WcfCustomAdapter.Inbound<NetTcpBindingElement>(
   a => {
         a.Address = new EndpointAddress("net.tcp://localhost/biztalk.factory/service.svc");
         a.Binding.MaxReceivedMessageSize = tenMegaBytes;
         a.Binding.ReaderQuotas.MaxArrayLength = tenMegaBytes;
         a.Binding.ReaderQuotas.MaxStringContentLength = tenMegaBytes;
         a.Binding.Security.Mode = SecurityMode.Transport;
         a.Binding.Security.Transport.ProtectionLevel = ProtectionLevel.Sign;
         a.Binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
         a.OpenTimeout = TimeSpan.FromMinutes(3);
});

Advanced Configuration Scenario

As you have seen in the Environment Overrides section, BizTalk.Factory’s Binding DSL supports multiple target deployment environments. Suppose that in the development and build environments you need to use a given transport, let us say HTTP —the stub you are using for testing purposes only supports HTTP— but in acceptance and production environments you have to use another transport, let us say net.tcp. Let us suppose further that you want to share the same action mapping and behavior configuration in all the environments to make sure the WCF channel and the Microsoft BizTalk Server® interaction flows will behave as expected.

As we saw earlier, configuring the behaviors requires to fall back on a WcfCustomAdapter. The following code sample illustrates how one could share the configuration commonalities between the various target deployment environments:

class IrsSendPort : SendPort<NamingConvention>
{
   public IrsSendPort()
   {
      Name = SendPortName.Towards("Irs").About("Anything").FormattedAs.Irrelevant;
      SendPipeline = new SendPipeline<PassThruTransmit>();
      ReceivePipeline = new ReceivePipeline<PassThruReceive>();
      var adapter = new WcfCustomAdapter.Outbound<BasicHttpBindingElement>(
         a => {
            a.Address = new EndpointAddress("http://localhost/soap-stub");
            a.Binding.MaxReceivedMessageSize = tenMegaBytes;
         });
      Transport.Adapter = ApplyAdapterCommonalities(adapter);
      Transport.Host = DummyHostResolutionPolicy.Default;
   }

   protected override void ApplyEnvironmentOverrides(string environment)
   {
      if (environment.IsAcceptanceUpwards())
      {
         var adapter = new WcfCustomAdapter.Outbound<NetTcpBindingElement>(
            a => {
               a.Address = new EndpointAddress("net.tcp://host/api/services/");
               a.Binding.MaxReceivedMessageSize = tenMegaBytes;
               a.Binding.Security.Mode = SecurityMode.Transport;
               a.Binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
               a.Binding.Security.Transport.ProtectionLevel = ProtectionLevel.EncryptAndSign;
            });
         Transport.Adapter = ApplyAdapterCommonalities(adapter);
      }
   }

   private IOutboundAdapter ApplyAdapterCommonalities<TAdapter>(TAdapter adapter)
      where TAdapter : IOutboundAdapter, IAdapterConfigEndpointBehavior, IAdapterConfigOutboundAction
   {
      adapter.EndpointBehaviors = new BehaviorExtensionElement[] {
         new CallbackDebugElement()
      };
      adapter.StaticAction = new ActionMapping {
         new ActionMappingOperation("operation", "action")
      };
      return adapter;
   }
}

Notice that the ApplyAdapterCommonalities method could have the following signature instead without any impact on the C# code and its semantics:

private IOutboundAdapter ApplyAdapterCommonalities<TBinding>(WcfCustomAdapter.Outbound<TBinding> adapter)
	where TBinding : StandardBindingElement, new()

The careful reader will have noticed that the ApplyAdapterCommonalities method does not handle the a.Binding.MaxReceivedMessageSize. The actual reason is that there is no common interface nor any common base class among BasicHttpBindingElement and NetTcpBindingElement —both directly inherit from StandardBindingElement— that define the MaxReceivedMessageSize property.

If the developer did not need to customize the behaviors, then he could have written the following code instead —but would be totally incapable of customizing the behaviors:

class SomePartySendPort : SendPort<NamingConvention>
{
   public SomePartySendPort()
   {
      Name = SendPortName.Towards("SomeParty").About("Anything").FormattedAs.Xml;
      SendPipeline = new SendPipeline<PassThruTransmit>();
      var adapter = new WcfBasicHttpAdapter.Outbound(a => { a.Address = new EndpointAddress("http://localhost/soap-stub"); });
      Transport.Adapter = ApplyAdapterCommonalities(adapter);
      Transport.Host = DummyHostResolutionPolicy.Default;
   }

   [SuppressMessage("ReSharper", "InvertIf")]
   protected override void ApplyEnvironmentOverrides(string environment)
   {
      if (environment.IsAcceptanceUpwards())
      {
         var adapter = new WcfNetTcpAdapter.Outbound(
            a => {
               a.Address = new EndpointAddress("net.tcp://host/api/services/");
               a.Identity = new IdentityElement { ServicePrincipalName = { Value = "spn" } };
               a.SecurityMode = SecurityMode.Transport;
               a.TransportClientCredentialType = TcpClientCredentialType.Windows;
               a.TransportProtectionLevel = ProtectionLevel.EncryptAndSign;
            });
         Transport.Adapter = ApplyAdapterCommonalities(adapter);
      }
   }

   private IOutboundAdapter ApplyAdapterCommonalities<TAdapter>(TAdapter adapter)
      where TAdapter : IOutboundAdapter, IAdapterConfigMaxReceivedMessageSize, IAdapterConfigOutboundAction
   {
      adapter.MaxReceivedMessageSize = tenMegaBytes;
      adapter.StaticAction = new ActionMapping {
         new ActionMappingOperation("operation", "action")
      };
      return adapter;
   }
}