More Binding Polymorphism

Last week I wrote about a generic method for getting the reader quotas from a binding.

 binding.GetProperty<XmlDictionaryReaderQuotas>(parameters);

This looks fine and it works for reading the quota values, but it doesn't work if you try to set a quota value. It turns out that this method doesn't actually get the correct reader quota object although it's hard to tell the difference sometimes. To see why, we need to take a closer look at what the code is doing. The GetProperty method of a binding works by creating a binding context and calling its GetInnerProperty method.

 context = new BindingContext(new CustomBinding(binding), parameters);
context.GetInnerProperty<XmlDictionaryReaderQuotas>();

A binding is turned into a custom binding by extracting the binding elements.

 context = new BindingContext(new CustomBinding(binding.CreateBindingElements()), parameters);
context.GetInnerProperty<XmlDictionaryReaderQuotas>();

In the case of creating a custom binding from another custom binding, this is just going to clone the binding element collection.

 context = new BindingContext(new CustomBinding(binding.Elements.Clone()), parameters);
context.GetInnerProperty<XmlDictionaryReaderQuotas>();

The GetInnerProperty method of a binding context works by cloning the binding context, taking off the topmost binding element, and calling GetProperty on the binding element.

 context = new BindingContext(new CustomBinding(binding.Elements.Clone()), parameters).Clone();
context.RemainingBindingElements.Remove<BindingElement>().GetProperty<XmlDictionaryReaderQuotas>(context);

We can trace the binding elements of the context back to the original set of binding elements to form this mouthful of an expression that works almost the same.

 elements = new BindingElementCollection(new BindingElementCollection(new CustomBinding(binding.Elements.Clone()).Elements));
elements.Remove<BindingElement>().GetProperty<XmlDictionaryReaderQuotas>(context);

By eliminating the redundancy, this becomes a much simpler expression.

 elements = new BindingElementCollection(binding.Elements.Clone());
elements.Remove<BindingElement>().GetProperty<XmlDictionaryReaderQuotas>(context);

When the element we're looking for is at the top, removing the topmost binding element of a copy of the collection is the moral equivalent of peeking at the original topmost binding element.

 elements = binding.Elements.Clone();
elements.Find<BindingElement>().GetProperty<XmlDictionaryReaderQuotas>(context);

It should now be clear what's going wrong. We eventually find a binding element that supports the reader quotas, but what we actually have is a copy of the original binding element. This means that we can read a copy of the settings, but any modifications to the copy will be lost. The solution is then really simple. We just need to remove that last copying process.

 elements = binding.Elements;
elements.Find<BindingElement>().GetProperty<XmlDictionaryReaderQuotas>(context);

We can almost go back to the beginning by substituting this change into past expressions.

 context = new BindingContext(new CustomBinding(binding.Elements), parameters);
context.GetInnerProperty<XmlDictionaryReaderQuotas>();

Since creating a custom binding from the binding elements of another custom binding isn't doing anything, we can simplify this last part as well.

 context = new BindingContext(binding, parameters);
context.GetInnerProperty<XmlDictionaryReaderQuotas>();

Now you have a generic way of accessing this setting that works for writing as well as reading.

Next time: Introducing CommunicationState

Comments