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 delegationAPI
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 byBizTalk.Factory
’sBinding 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 REST
ful interactions through parameterized URL
s 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 REST
ful 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-inWCF
-based adapters do not allow injecting custom behavior components but come with predefined ones. As a result, theWCF
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 theWcfCustomAdapter
orWcfCustomIsolatedAdapter
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
, orWcfSqlAdapter
, as well as theWcfWebHttpAdapter
are noticeable exceptions that always allow theWCF
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;
}
}