This post is part of a series about WCF extensibility points. For a list of all previous posts and planned future ones, go to the index page.
After a heavy subject in the channels, this week’s post is about something simpler – configuration extensions for bindings and binding elements. Just like you can use extensions to define endpoint and service behaviors in configuration, both binding elements (to be used in custom bindings) and full binding settings can be defined using configuration as well, as long as we have the appropriate binding element and standard binding extensions in place. And despite my personal bias against configuration, the scenario I’ll show in this post is actually a perfect example where defining a binding in configuration can be a good choice.
Binding elements can be made “configurable” by using the BindingElementExtensionElement class (similar to the BehaviorExtensionElement class covered in a previous post). Bindings take a little more work, since we need two extension classes, one for the binding “section” (StandardBindingCollectionElement<TStandardBinding, TBindingConfiguration>) for the new binding type, and one for each specific binding “instance” (StandardBindingElement). The next sections will have more details about these classes.
Public implementations in WCF
Those are the public binding element extensions – those which can be used inside custom binding declarations in configuration. I’ll divide them in sections for the channel types which those binding elements create.
- BinaryMessageEncodingElement (<binaryMessageEncoding>): Adds a BinaryMessageEncodingBindingElement to the custom binding, defining that the communication will be encoded via the .NET Binary Format.
- ByteStreamMessageEncodingElement (<byteStreamMessageEncoding>, new in 4.0): Adds a ByteStreamMessageEncodingBindingElement to the custom binding, defining the message encoding as a stream of bytes. Useful for services which need access to the raw transport payload.
- MtomMessageEncodingElement (<mtomMessageEncoding>): Adds a MtomMessageEncodingBindingElement to the custom binding, defining the message encoding to be the Message Transmission Optimization Mechanism (MTOM).
- TextMessageEncodingElement (<textMessageEncoding>): Adds a TextMessageEncodingBindingElement to the custom binding, defining that the communication will be encoded via “normal” XML.
- WebMessageEncodingElement (webMessageEncoding, new in 3.5): Adds a WebMessageEncodingBindingElement to the custom binding. Since this is a “composite” encoder, the communication can be made via XML, JSON or the Raw mode.
- PeerTransportElement (<peerTransport>): Adds a PeerTransportBindingElement to the custom binding. Used for peer-to-peer communication.
- NamedPipeTransportElement (<namedPipeTransport>): Adds a NamedPipeTransportBindingElement to the custom binding. Used for same-machine communication.
- TcpTransportElement (<tcpTransport>): Adds a TcpTransportBindingElement to the custom binding. Defines the communication to go over TCP according to the .NET Message Framing rules.
- HttpTransportElement (<httpTransport>): Adds a HttpTransportBindingElement to the custom binding. Defines that the communication will happen over unencrypted HTTP.
- HttpsTransportElement (<httpsTransport>): Adds a HttpsTransportBindingElement to the custom binding. Defines that the communication will happen over HTTPS (HTTP over SSL).
- MsmqIntegrationElement (<msmqIntegration>): Adds a MsmqIntegrationBindingElement to the custom binding. Defines that the communication will happen through MSMQ using one of the legacy protocols, such as COM, MSMQ native APIs or the System.Messaging APIs.
- MsmqTransportElement (<msmqTransport>): Adds a MsmqTransportBindingElement to the custom binding. Defines that the communication will happen through MSMQ using the native Message Queueing (MSMQ) protocol.
- CompositeDuplexElement (<compositeDuplex>): Adds a CompositeDuplexBindingElement to the custom binding. Adds a callback channel to the binding to be used in duplex communication.
- OneWayElement (<oneWay>): Adds an OneWayBindingElement to the custom binding. Defines a channel which changes the shape of a request/reply inner channel to input / output (on client / server, respectively).
- PnrpPeerResolverElement (<pnrpPeerResolver>): Adds a PnrpPeerResolverBindingElement to the custom binding. Defines a channel which implements the Peer Name Resolution Protocol for peer-to-peer communication.
- ReliableSessionElement (<reliableSession>): Adds a ReliableSessionBindingElement to the custom binding. Adds a channel which changes the shape of a request/reply (or input/output) inner channel to request session / reply session (or input session / output session) by implementing the WS-RM protocol.
- SecurityElement (<security>): Adds a security binding element to the custom binding. The actual binding element type depends on the “mode” attribute of the <security> element.
- SslStreamSecurityElement (<sslStreamSecurity>): Adds a SslStreamSecurityBindingElement to the custom binding. Adds SSL support to an unencrypted channel (i.e., TCP).
- TransactionFlowElement (<transactionFlow>): Adds a TransactionFlowBindingElement to the custom binding. Defines the transaction flow support for the binding.
- UseManagedPresentationElement (<useManagedPresentation>): Adds a UseManagedPresentationBindingElement to the custom binding. It’s used mostly by CardSpace.
- WindowsStreamSecurityElement (<windowsStreamSecurity>): Adds a WindowsStreamSecurityBindingElement to the custom binding. Adds Windows security to the channel.
- DiscoveryClientElement (<discoveryClient>, new in 4.0): Adds a DiscoveryClientBindingElement to the custom binding. Used in client applications to allow them to access discoverable services without knowing their endpoint address in advance.
Those are public standard binding extensions.
- BasicHttpBindingElement (<basicHttpBinding>): Defines configurations for the BasicHttpBinding.
- MexHttpBindingElement (<mexHttpBinding>): Defines configurations for a binding for the WS-MessageExchange (MEX) protocol over HTTP.
- MexHttpsBindingElement (<mexHttpsBinding>): Defines configurations for a binding for the WS-MessageExchange (MEX) protocol over HTTPS.
- MexNamedPipeBindingElement (<mexNamedPipeBinding>): Defines configurations for a binding for the WS-MessageExchange (MEX) protocol over named pipes.
- MexTcpBindingElement (" href="http://msdn.microsoft.com/en-us/library/aa967279.aspx">" href="http://msdn.microsoft.com/en-us/library/aa967279.aspx"><mexTcpBinding> ): Defines configurations for a binding for the WS-MessageExchange (MEX) protocol over TCP.
- MsmqIntegrationBindingElement (<msmqIntegrationBinding>): Define configurations for the MsmqIntegrationBinding.
- NetMsmqBindingElement (<netMsmqBinding>): Define configurations for the NetMsmqBinding.
- NetNamedPipeBindingElement (" href="http://msdn.microsoft.com/en-us/library/ms731291.aspx">" href="http://msdn.microsoft.com/en-us/library/ms731291.aspx"><netNamedPipeBinding>): Define configurations for the NetNamedPipeBinding.
- NetPeerTcpBindingElement (<netPeerTcpBinding>): Define configurations for the NetPeerTcpBinding.
- NetTcpBindingElement (<netTcpBinding>): Define configurations for the NetTcpBinding.
- WebHttpBindingElement (" href="http://msdn.microsoft.com/en-us/library/bb412176.aspx">" href="http://msdn.microsoft.com/en-us/library/bb412176.aspx"><webHttpBinding>, new in 3.5):Define configurations for the WebHttpBinding.
- WS2007FederationHttpBindingElement (<ws2007FederationHttpBinding>, new in 3.0SP1): Define configurations for the WS2007FederationHttpBinding.
- WS2007HttpBindingElement (<ws2007HttpBinding>, new in 3.0SP1): Define configurations for the WS2007HttpBinding.
- WSDualHttpBindingElement (<wsDualHttpBinding>): Define configurations for the WSDualHttpBinding.
- WSFederationHttpBindingElement (<wsFederationHttpBinding>): Define configurations for the WSFederationHttpBinding.
- WSHttpBindingElement (<wsHttpBinding>): Define configurations for the WSHttpBinding.
- CustomBindingElement (<customBinding>): Define configurations for a CustomBinding, where the binding elements are explicitly defined.
The collection classes for those bindings above are located in the same namespace (and assembly) as their respective binding classes.
To implement a binding element configuration extension, one needs to derive from BindingElementExtensionElement. The derived class needs to override the BindingElementType property to return the type of the binding element which that extension can create, and also the CreateBindingElement method, to actually return an instance of the binding element it configures. Notice that BindingElementExtensionElement is derived from System.Configuration.ConfigurationElement, so the class can (and should) use the ConfigurationPropertyAttribute to define attributes and sub-elements which need to be passed to the binding element at creation time.
To implement a new “standard binding” type, one needs first to derive from StandardBindingElement, and again override two members: the BindingElementType property (which should be called “BindingType”, since this doesn’t deal with binding elements, but I digress) to return the type of binding which will be created, and OnApplyConfiguration, which will be called with an uninitialized instance of the binding type to be set. This class is also derived from System.Configuration.ConfigurationElement, but unlike on the binding element extensions, I always have to override the Properties property as well to include the custom properties for that binding (it’s not automatically picked up by the runtime). Also notice that since a “blank” instance of the binding element is passed to the extension (instead of the extension creating one), the standard binding class needs to have a parameter-less constructor, otherwise it cannot be defined in configuration. Finally, we need to define a “collection element” for the new standard binding, deriving a class from StandardBindingCollectionElement<TStandardBinding, TBindingConfiguration>. This step is actually quite simple, as the base class (with generic parameters) does most of the legwork, and all one needs to do is to define the parameters as the new standard binding type and the standard binding configuration element type which will be used. The code example will show this in practice.
How to add binding element and binding extensions
Binding element: custom binding element extensions are added to the <system.serviceModel / extensions / bindingElementExtensions> element, and given an alias which can be used inside custom binding declarations:
Standard bindings: standard binding extensions are added to the <system.serviceModel / extensions / bindingExtensions> element, and given an alias which can be used as a child element of the <bindings> node. The type must be the assembly-qualified name of the class derived from StandardBindingCollectionElement, not the one derived from StandardBindingElement.
One note about extensions and Visual Studio: just like it happened with behavior extensions, VS will usually issue a warning about a schema violation when we use binding or binding element configuration extensions, and tag the extension with a squiggly line (see below). The warning states that it is not a valid child for the <binding> or <customBinding> elements. This is just a nuisance, as this error can be safely ignored and won’t cause any problems during runtime. But if you’re someone who gets bugged by warnings (or has a setting in the project to treat all warnings as errors, you can update the configuration schema in Visual Studio at \Program Files\Microsoft Visual Studio 10.0\Xml\Schemas\DotNetSchema.xsd (replace Program Files with Program Files (x86) for 64-bit OS, and replace 10.0 with the appropriate VS version) and update the schema to allow for this new element as well.
Real world example: configuration extensions for the “chaos monkey channel”
The “chaos monkey channel”, introduced in the post about channels, is great during testing of the system for resiliency over failures on dependent services. But as a system matures (or as it goes from the test to the production stage), one may want to tweak the amount of failures which the channel injects into the system. With a code-based binding element (as it was in the first sample), one would need to change the source code, then recompile it for every change in the “chaos controller”. Or one would simply add some external variable (i.e., registry, file, database, etc.) which would be read by the runtime when creating the channel. WCF configuration is nothing more than another external source, it just happens to be fairly well used (the .NET configuration system has been in use since the beginning of .NET), so it’s a good place for it to be. This is one of the (few, IMO) places which configuration actually makes sense.
And before I go further, the usual disclaimer: this is a sample for illustrating the topic of this post, this is not production-ready code. I tested it for a few scenarios and it worked, but I cannot guarantee that it will work for all scenarios. It also does not have a lot of error handling / good exception messages which a production-level code should have, as I’ve kept it simple for illustration purposes.
The project starts with the “Chaos Monkey Channel”. I won’t go into the details of that project, only on the configuration extensions this time. Just one small modification: the sample chaos controller was “promoted” from a nested class to a top-level one, and it got two parameters which can be passed to its constructor (to illustrate parameters in the configuration extensions).
Now we can start with the extension for the binding element. The class is actually quite simple, with a pair of properties to control both the random seed and the level of failure introduced by the channel. Those properties are picked up automatically by the configuration subsystem, so they can simply be used in the CreateBindingElement method.
And with it we can register this new extension class in the <system.serviceModel / extensions / bindingElementExtensions> list, as shown below.
And we can now create a new custom binding in code which uses the new binding element (<chaosMonkey>), and use it in a program.
And that’s it – the ChaosMonkeyBindingElement can now be defined in configuration. For the standard binding, if we’re using a certain binding element combination in a custom binding ofrten enough, sometimes we create a new Binding class so that we don’t have to always define the same custom binding over and over. In this example, I’ll create a new binding which has a chaos monkey element, plus a binary encoding, plus the HTTP transport.
This new binding can be used in code, but since this is a configuration post, let’s make it configurable as well. First off, the StandardBindingElement subclass for it. As I mentioned before, if I don’t override the “Properties” property, this does not work (unlike in the binding element extension, where it just worked).
Finally the standard binding collection element. This is essentially a one-liner, as the base class does all the work:
Now we can register that extension in the <system.serviceModel / extensions / bindingExtensions> list:
And finally, we can use it in our test program:
And that’s it for configuration extensions.
[Code in this post]