Extending WSDL and Policy, Part 2. Importing custom policy assertions.
Yesterday I outlined how to export custom policy assertions and why you would do such a thing. One thing I failed to note explicitly, but that should be obvious, is that policy assertions are merely XML elements. At a fundamental level, that's all they are. So in addition to conveying binding-related information, they can also convey pretty much any other information as well. That's not what it's for; but still. :-)
Tonight I'll discuss importing custom policy. Recall that policy assertions are to convey information about binding implementations. Therefore the point of importing policy assertions is to detect a certain policy and respond by adding a binding element, a binding, or configuring a binding or binding element to support the policy assertion. However, unlike in the service application, the importer does not need to be implemented on any particular binding element. In fact, it can be implemented on any ol' object at all. And to implement it, all you do is implement -- it should be clear -- System.ServiceModel.Description.IPolicyImportExtension. The external documentation fairly accurately describes the process, although I note that the example is missing (I'm here to oblige) and it looks like some text has vanished. I'll get right on that, just after I finish writing the topic on how to understand sessions, the documentation for which is currently just plain wrong in many ways.
The algorithm is
- Locate the assertion you are going to respond to at the scope you are interested (binding-wide assertion, operation-wide assertion, or a message-wide assertion).
- Add or modify binding elements by using the BindingElements property on the PolicyConversionContext.
- Remove the policy assertion from the assertion collection.
If you don't remove the assertion, the binding you were trying to modify will not be imported. All your hard import work will have been done in vain.
Here's an example implementation that does NOT add a binding. We could also retrieve a binding and modify it in some way, too. Note the remove step in red.
using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.Text;
using System.Xml;
namespace Microsoft.WCF.Documentation
{
public class CustomPolicyImporter : IPolicyImportExtension
{
#region IPolicyImporter Members
public const string name1 = "acme";
public const string ns1 = "https://Microsoft/WCF/Documentation/CustomPolicyAssertions";
/*
* Importing policy assertions usually means modifying the bindingelement stack in some way
* to support the policy assertion. The procedure is:
* 1. Find the custom assertion to import.
* 2. Insert a supporting custom bindingelement or modify the current binding element collection
* to support the assertion.
* 3. Remove the assertion from the collection. Once the ImportPolicy method has returned,
* any remaining assertions for the binding cause the binding to fail import and not be
* constructed.
*/
public void ImportPolicy(MetadataImporter importer, PolicyConversionContext context)
{
Console.WriteLine("The custom policy importer has been called.");
// Locate the custom assertion.
XmlElement customAssertion = context.GetBindingAssertions().Find(name1, ns1);
if (customAssertion != null)
{
context.GetBindingAssertions().Remove(customAssertion);
Console.WriteLine(
"Removed our custom assertion from the imported "
+ "assertions collection and inserting our custom binding element."
);
// Here we would add the binding modification that implemented the policy.
// This sample does not do this.
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(customAssertion.NamespaceURI + " : " + customAssertion.Name);
Console.WriteLine(customAssertion.OuterXml);
Console.ForegroundColor = ConsoleColor.Gray;
}
}
#endregion
}
}
Note that that this is just a class. It's not a binding element or anything special. And to wire it up in configuration, you simply do this:
<client>
<endpoint
address="https://localhost:8080/StatefulService"
binding="wsHttpBinding"
bindingConfiguration="CustomBinding_IStatefulService"
contract="IStatefulService"
name="CustomBinding_IStatefulService" />
<metadata>
<policyImporters>
<extension type="Microsoft.WCF.Documentation.CustomPolicyImporter, PolicyExtensions"/>
</policyImporters>
</metadata>
</client>
And that's it. Tomorrow I'll move on to customizing WSDL export and import.
Hope it helps, me.