A few days ago I ranted a bit about the Binding object model in WCF and how restrictive it feels when I want to imperatively describe my service bindings without being tied down to a specific transport. If I want to disable security or set the maximum allowed size per field in a message, I have to write identical-looking code for each different binding – something that just begs for refactoring which can't be done.
Luckily, the WCF team bloggers are ever alert, and a few days later I got a response from Nicholas Allen describing the reasons behind this behavior. His explanantion consisted of two parts, and I'd like to address them seperately:
1) Avoiding meaningless abstractions.
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.
Granted, one of my common failings is the urge for over-abstraction. I've read Spolsky and I've read Gunnerson, and I try to avoid excessive generalizations, but somtimes it gets the better of me. Having the security settings share a common base – even if only to allow setting No Security in a shared way – seems intuitive. I'll bow down to the WCF team's decision here – if the binding security objects really don't share a common base, there's no reason to abstract them together.
2) Aggregation, not inheritance.
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:
(new BindingParameterCollection()).MaxArrayLength = 2;
Instead of relying on inheritance for shared properties, the object model views each Binding object as an aggregate of several binding elements and properties. Since the properties of a binding can differ between two instances of the same transport, the GetProperty
The first thing that came to my mind about this is the similarity to the way C++ code worked with COM objects. You used the shared IUnknown interface to call QueryInterface and see what interfaces were supported by the object, and then got a handle to those interfaces. This is similar – the GetProperty
This, however, led to this question: why wasn't this functionality also implemented with interfaces? Why couldn't we have an ISupportsReaderQuotas interface which defines the XmlDictionaryReaderQuotas property, and instead of calling GetProperty() I can cast my binding to the interface and work with that? Each binding can implement the interfaces that make sense to it, and I can use the existing .NET mechanisms (the is and as operators, as well as reflection support) to query the object for the required operations.
This will also bypass other limitations of this model. As it stands, I have no idea how to access the MaxReceivedMessageSize property on the binding – it's an Int64 seperately defined in each binding class. GetProperty
The advantages to the GetProperty
Thinking it over (this is a stream-of-consciousness blog, can't you tell?), I can certainly see the logic behind the GetProperty
(This actually gives me an interesting idea about combining interfaces and aggregation, but more on that later)
So, assuming you made it to the end – how do you feel about the current implementation? Am I completely off-base here? Do I make a valid point? Were these issues discussed internally before this implementation were chosen? What were the pros and cons?
I'd love to hear more opinions, both from anyone on the WCF that might be listening and from anyone with an opinion.