Binding Polymorphism

A few days ago, Avner Kashtan asked why the design-time elements of the channel stack don't have a common object hierarchy for configuration settings (thereby making it difficult to change settings without knowing what transport you're using). The answer turns out to be extremely pragmatic.

First, some corrections.

The article contains a layered binding model, which seems to continue lingering in the documentation despite its lack of correctness. There's really only one rule for bindings. A binding must contain a transport. Everything else is more common sense than an actual requirement. For instance, if you go to the trouble of signing, encrypting, and creating a cryptographic hash for a message header, then it doesn't make sense to later have a binding element whose job it is to change that header. This leads to rules of thumb, such as that the message security binding element is below the transaction binding element, but that doesn't actually stop you from trying to build and use that binding.

Also, message encoders are optional despite our very strong guidance that you should always use a message encoder. You can build and use a binding without a message encoder assuming that the transport is kind enough to give you one by default. The transport might not even have the concept of a message encoder. How would you encode messages for a .NET Remoting transport? It doesn't make sense to talk about encoding in that transport.

Now, the answer.

There are configuration settings that don't have a common object hierarchy because it turns out that despite being intuitively similar, they neither look nor act the same. Take the example of security configuration on a transport. There are entirely incompatible security objects called NetTcpSecurity and NetNamedPipeSecurity. The only overlap in configuring these two transports happens to be when security is entirely disabled. That's not a very interesting abstraction to make. HTTP security is actually nothing at all like either TCP security or named pipe security. These transports simply have different feature sets.

Then, there are some configuration settings that do look and act the same. For the settings that apply to every channel in the binding, those settings are available on the binding itself. For the settings that apply to a specific channel, you don't know what in the channel stack is actually implementing those settings. We have a mechanism for dealing with this problem called GetProperty. For instance, if you want to set an XML reader quota in a generic fashion, you would use

binding.GetProperty<XmlDictionaryReaderQuotas>(new BindingParameterCollection()).MaxArrayLength = 2;

new BindingContext(binding, new BindingParameterCollection()).GetInnerProperty<XmlDictionaryReaderQuotas>().MaxArrayLength = 2

Update: I found a small piece on binding element stacking rules of thumb that I wrote earlier. I'll try to clean that up and post it next week as a counterpoint to the layered binding model.

Update: I'll explain in an upcoming post where and why that uglier version of GetProperty comes from.

Comments (8)

  1. A few days ago I ranted a bit about the Binding object model in WCF and how restrictive it feels when

  2. A few days ago I ranted a bit about the Binding object model in WCF and how restrictive it feels when…

  3. I’ve been reading and digging into the BindingElement stacking and ran into some unanswered questions:

    1) The code you mentioned above seems to return null, always. Digging with Reflector I can see where each BindingElement calls the BindingContext’s GetInnerProperty<T> and where the binding context calls each BindingElement’s GetProperty<T>, but there doesn’t seem to be any code that fetches the actual property. What am I missing?

    2) Is there any way to replace a BindingElement in a given binding? Say, take a NetTcpBinding and replace its BinaryMessageEncoderBindingElement with the CompressionEncoderBindingElement from the SDK samples? From my tests it seems that if I imperatively declare my mindings I need to create a brand new CustomBindings and feed it the BindingCollection.

  4. 1) What should happen in this case is that you crawl all the way down to the transport binding element.  The transport then either fishes the message encoder binding element out or provides its own quota settings.  There appears to be some weirdness here (I get quota settings but they aren’t the right ones) so I’ll investigate this more.

    2) Your tests are correct.  You need to get the collection of binding elements, make the changes you want, and then create a CustomBinding from the colleciton.

  5. Last week I wrote about a generic method for getting the reader quotas from a binding . binding.GetProperty&lt;XmlDictionaryReaderQuotas&gt;(parameters);

Skip to main content