Creating Classes that Implement and Support a Custom Policy
DCS is an extension of Windows Communication Foundation (WCF), and the mechanism used by DCS to inject a policy into a channel is very similar to that employed by WCF. To define a custom policy for a DCS service, you must implement the following items:
- A class that implements the policy
- A channel filter class that enforces the policy and that can be added to the WCF request and response channels
- A custom binding element to enable a client application and a service to configure the policy
- A custom binding element extension to enable the custom binding element to be accessed from a configuration file
The following sections describe how to perform these tasks.
Note: |
|---|
| WCF provides a set of interfaces that you can use to build custom policies. However, DCS includes a set of base classes that implement much of the core functionality required when you create custom policy classes. This functionality is tuned to the DCS infrastructure. Therefore, you should use the DCS base classes rather than the WCF interfaces. |
- Start Microsoft® Visual Studio® development system, and create a class library project.
- Add the following assembly references to the project:
- Microsoft.ConnectedIndustry.ServiceModel.dll
- Microsoft.ConnectedIndustry.ServiceModel.Common.dll
- Modify the class in the class library project to inherit from the Microsoft.ConnectedIndustry.ServiceModel.Policy.BasePolicy<TPolicy> generic class. For more information, see The BasePolicy<TPolicy> Class.
- Decorate the class with the System.Xml.XmlType attribute and specify the name of the policy as the TypeName property.
- Add any custom properties required by the policy, decorate them with the System.Xml.XmlAttribute attribute, and specify the name of each property as the AttributeName property of XmlAttribute.
- Override the PolicyDefinition property to return a string that contains a unique XML-qualified name (XML name and namespace) that identifies the policy.
The following code example shows a simple custom policy named SamplePolicy. This policy exposes a single Boolean property named enable.
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.ConnectedIndustry.ServiceModel.Policy;
using System.Xml.Serialization;
using System.Xml;
using Microsoft.ConnectedIndustry.ServiceModel.Common;
namespace DCS.Samples.SamplePolicy
{
[XmlType(samplePolicyName)]
public class SamplePolicy : Microsoft.ConnectedIndustry.ServiceModel.Policy.BasePolicy<SamplePolicy>
{
internal const string samplePolicyName = "samplePolicy";
internal const string samplePolicyNamespace = "http://schemas.Microsoft.com/ConnectedIndustry/ServiceModel/2008/05/Policy";
internal const string samplePolicyPrefix = "sample";
[XmlAttribute("enable")]
public bool Enable;
public override string PolicyDefinition
{
get
{
return InternalPolicyDefinition;
}
}
internal static string InternalPolicyDefinition
{
get
{
return new XmlQualifiedName(samplePolicyName,
Namespaces.PolicyNamespace).ToString();
}
}
}
}
The next code example shows the ContextPolicy class implemented by DCS. This policy class exposes two Boolean properties named enableContext and requireContext.
[XmlType("contextPolicy")]
public class ContextPolicy : BasePolicy<ContextPolicy>
{
// Fields
internal const string contextPolicyName = "contextPolicy";
internal const string contextPolicyNamespace = "http://schemas.Microsoft.com/ConnectedIndustry/ServiceModel/2008/05/Policy";
internal const string contextPolicyPrefix = "dcs";
[XmlAttribute("enableContext")]
public bool EnableContext;
[XmlAttribute("requireContext")]
public bool RequireContext;
// Properties
internal static string InternalPolicyDefinition
{
get
{
return new XmlQualifiedName("contextPolicy", "http://schemas.Microsoft.com/ConnectedIndustry/ServiceModel/2008/05/Policy").ToString();
}
}
public override string PolicyDefinition
{
get
{
return InternalPolicyDefinition;
}
}
}
- Create a new class that implements the Microsoft.ConnectedIndustry.ServiceModel.ProtocolChannels.Channels.IChannelFilter interface. For more information, see The IChannelFilter Interface.
- Implement the ProcessOutgoingRequest method. This method takes a ChannelFilterContext object named context as a parameter. The client application calls this method when it is ready to send a request to a service that implements the custom policy. Typically, you should add code to this method that performs the following tasks:
- Retrieve the policy object that contains the definition of the policy. You can use the static TryDecodeAssertion method of the custom policy class (inherited from the BasePolicy<TPolicy> generic class) to obtain this object. Pass the value of the expression context.CurrentAssertion.OuterXml as the parameter to the TryDecodeAssertion method.
- Examine the properties of the policy object, and take any actions required to conform to these settings. For example, if the properties specify that one or more fields in the outgoing request message must be encrypted by using a specified key, then perform the encryption. The request message is available as the RequestMessage property of the context parameter passed to the ProcessOutgoingRequest method.
- Implement the ProcessIncomingRequest method. This method takes a ChannelFilterContext object named context as a parameter. The service calls this method when a request message arrives for processing. Typically, you add code to this method to perform the following tasks:
- Retrieve the policy object that contains the definition of the policy. You can use the static TryDecodeAssertion method of the custom policy class to obtain this object. Pass the value of the expression context.CurrentAssertion.OuterXml as the parameter to the TryDecodeAssertion method.
- Examine the properties of the policy object, and verify that the request message conforms to these settings. Throw an exception if the request message does not conform. The request message is available as the RequestMessage property of the context parameter passed to the ProcessIncomingRequest method.
- Implement the ProcessOutgoingReply method. This method takes a ChannelFilterContext object named context as a parameter. The service calls this method when it sends a response message to the client application. Typically, you add code to this method that performs the following tasks:
- Retrieve the policy object that contains the definition of the policy. You can use the static TryDecodeAssertion method of the custom policy class to obtain this object. Pass the value of the expression context.CurrentAssertion.OuterXml as the parameter to the TryDecodeAssertion method.
- Examine the properties of the policy object, and take any actions required to conform to these settings. The response message is available as the ResponseMessage property of the context parameter passed to the ProcessOutgoingReplymethod.
- Implement the ProcessIncomingReply method. This method takes a ChannelFilterContext object named context as a parameter. The client application calls this method when it receives a response message from a service. Typically, you add code to this method that performs the following tasks:
- Retrieve the policy object that contains the definition of the policy. You can use the static TryDecodeAssertion method of the custom policy class to obtain this object. Pass the value of the expression context.CurrentAssertion.OuterXml as the parameter to the TryDecodeAssertion method.
- Examine the properties of the policy object, and verify that the response message conforms to these settings. Throw an exception if the response message does not conform. The response message is available as the ResponseMessage property of the context parameter passed to the ProcessIncomingReply method.
- Implement the Clone method. This method returns a copy of the IChannelFilter object.
The next code example shows the ContextChannelFilter class implemented by DCS. This channel filter class supports the ContextPolicy policy class.
The ProcessOutgoingRequest method verifies that a DCS context is provided with the request if the RequireContext property of the policy is true, and throws an ArgumentNullException exception otherwise. If a DCS context is provided, then the ProcessOutgoingRequest method adds it to the header of the request message.
The ProcessIncomingRequest method verifies that the incoming request message contains a context in the message header if the RequireContext property of the policy is true, and throws a ProtocolException exception if no context is supplied. If a DCS context is provided, then the ProcessIncomingRequest method retrieves it from the message header and sets the static Current property of the Context class to the context. The business logic in operations implemented by the service can access the context through this property.
public class ContextChannelFilter : IChannelFilter
{
// Fields
internal const string cffContextNamespace = "http://schemas.Microsoft.com/ConnectedIndustry/ServiceModel/2008/05/Context";
internal const string cffContextPropertyName = "http://schemas.Microsoft.com/ConnectedIndustry/ServiceModel/2008/05/Context";
internal const string contextHeaderName = "context";
private const string moduleName = "ContextChannelFilter";
// Methods
public IChannelFilter Clone()
{
return new ContextChannelFilter();
}
private static ContextPolicy GetContextPolicy(ChannelFilterContext context)
{
ContextPolicy policy = null;
if (context.MustActivate)
{
policy = new ContextPolicy();
policy.EnableContext = true;
policy.RequireContext = true;
return policy;
}
XmlElement assertion = context.CurrentAssertion;
if (assertion != null)
{
string assertionValue = assertion.OuterXml;
Tracer.Write(TraceEventType.Verbose, "ContextChannelFilter", "Trying to decode assertion value: {0}", new object[] { assertionValue });
policy = BasePolicy<ContextPolicy>.TryDecodeAssertion(assertionValue);
}
return policy;
}
public void ProcessIncomingReply(ChannelFilterContext context)
{
}
public void ProcessIncomingRequest(ChannelFilterContext context)
{
ContextPolicy policy = GetContextPolicy(context);
if (policy != null)
{
if (policy.EnableContext)
{
Message message = context.RequestMessage;
Context messageContext = null;
int position = message.Headers.FindHeader("context", "http://schemas.Microsoft.com/ConnectedIndustry/ServiceModel/2008/05/Context");
if (position < 0)
{
if (policy.RequireContext)
{
Tracer.Write(TraceEventType.Error, "ContextChannelFilter", Resources.ContextHeaderNotFound);
throw new ProtocolException(Resources.ContextHeaderNotFound);
}
Tracer.Write(TraceEventType.Verbose, "ContextChannelFilter", Resources.ContextNotRequired);
}
else
{
Type contextType = ContextFactory.ContextType;
Tracer.Write(TraceEventType.Verbose, "ContextChannelFilter", "Using context type {0}", new object[] { contextType.FullName });
DataContractSerializer contextSerializer = new DataContractSerializer(contextType, "context", "http://schemas.Microsoft.com/ConnectedIndustry/ServiceModel/2008/05/Context", null, 0x7fffffff, false, false, null);
messageContext = message.Headers.GetHeader<Context>(position, contextSerializer);
Tracer.Write(TraceEventType.Verbose, "ContextChannelFilter", Resources.ContextHeaderFound);
message.Headers.RemoveAt(position);
Context.Current = messageContext;
message.Properties.Add("http://schemas.Microsoft.com/ConnectedIndustry/ServiceModel/2008/05/Context", messageContext);
Tracer.Write(TraceEventType.Information, "ContextChannelFilter", Resources.ContextDefined);
}
}
else
{
Tracer.Write(TraceEventType.Verbose, "ContextChannelFilter", Resources.ContextNotEnabled);
}
}
else
{
Tracer.Write(TraceEventType.Verbose, "ContextChannelFilter", Resources.ContextNoPolicy);
}
}
public void ProcessOutgoingReply(ChannelFilterContext context)
{
}
public void ProcessOutgoingRequest(ChannelFilterContext context)
{
ContextPolicy policy = GetContextPolicy(context);
if (policy != null)
{
if (policy.EnableContext)
{
Message message = context.RequestMessage;
Context currentContext = Context.Current;
if (currentContext == null)
{
if (policy.RequireContext)
{
Tracer.Write(TraceEventType.Error, "ContextChannelFilter", Resources.ContextIsNull);
throw new ArgumentNullException(Resources.ContextIsNull);
}
Tracer.Write(TraceEventType.Verbose, "ContextChannelFilter", Resources.ContextNotRequired);
}
else
{
if (currentContext.Properties.ContainsKey("businessLogId"))
{
currentContext.Properties.Remove("businessLogId");
Tracer.Write(TraceEventType.Verbose, "ContextChannelFilter", "Removed businessLogId from context properties");
}
Type contextType = ContextFactory.ContextType;
Tracer.Write(TraceEventType.Verbose, "ContextChannelFilter", "Using context type {0}", new object[] { contextType.FullName });
DataContractSerializer contextSerializer = new DataContractSerializer(contextType);
MessageHeader header = MessageHeader.CreateHeader("context", "http://schemas.Microsoft.com/ConnectedIndustry/ServiceModel/2008/05/Context", currentContext, contextSerializer);
int position = message.Headers.FindHeader("context", "http://schemas.Microsoft.com/ConnectedIndustry/ServiceModel/2008/05/Context");
if (position >= 0)
{
message.Headers.RemoveAt(position);
}
message.Headers.Add(header);
Tracer.Write(TraceEventType.Information, "ContextChannelFilter", Resources.ContextHeaderAdded);
}
}
else
{
Tracer.Write(TraceEventType.Verbose, "ContextChannelFilter", Resources.ContextNotEnabled);
}
}
else
{
Tracer.Write(TraceEventType.Verbose, "ContextChannelFilter", Resources.ContextNoPolicy);
}
}
}
- Create a new class that inherits from the Microsoft.ConnectedIndustry.ServiceModel.ProtocolChannels.Channels.BaseBindingElement class and that implements the Microsoft.ConnectedIndustry.ServiceModel.Policy.IPolicyEnabledBindingElement interface. For more information, see The BaseBindingElement Class and The IPolicyEnabledBindingElement Interface.
- Implement the ConfigureEndpointDescription method of the IPolicyEnabledBindingElement interface. The ExtendedServiceFactory.CreateServiceHost method (part of the DCS infrastructure) calls this method to update the endpoint with the custom policy. In the ConfigureEndpointDescription method, add the custom policy to the current custom policy binding element so that the binding element can export it.
- The ConfigureEndpointDescription method takes two parameters: a System.ServiceModel.Description.ServiceEndpoint object named endpointDescription that represents the endpoint to be constructed and a Microsoft.ConnectedIndustry.ServiceModel.Policy.ServicePolicies object named servicePolicies that contains a list of the service-level and operation-level policies for the service endpoint. Typically, you use the ConfigureEndpointDescription to perform the following tasks:
- Retrieve the custom policy object from the servicePolicies object. You can use the static TryDecodeAssertion method of the custom policy class (inherited from the BasePolicy<TPolicy> generic class) to obtain this object. Pass the value of the expression servicePolicies.Policies[name].PolicyAssertion.Assertion.OuterXml as the parameter to the TryDecodeAssertion method, where name is a string containing the XML qualified name (name and namespace) of the custom policy.
- Create a new System.ServiceModel.Channels.CustomBinding object , which passes the expression endpointDescription.Binding as the parameter to the constructor.
- Create a new instance of the custom binding element class by using the Elements.Find<custom binding element type> method of the custom binding object, where custom binding element is the type of the custom binding element class.
- Add the custom policy object to the custom binding element.
- Set the Binding property of the endpointDescription parameter to the custom binding element.
- Implement the ConfigureFaultDescription method of the IPolicyEnabledBindingElement interface. The ExtendedServiceFactory.CreateServiceHost method calls the ConfigureFaultDescription method for every fault that a service endpoint can raise. You can use the ConfigureFaultDescription method to modify the fault description that occurs. You can leave the body of this method empty of there is no fault policy defined for the fault message.
- The ConfigureFaultDescription method takes two parameters: a System.ServiceModel.Description.FaultDescription object that represents the fault, and a Microsoft.ConnectedIndustry.ServiceModel.Policy.PolicyAssertion object that represents the custom policy.
- Implement the ConfigureMessageDescription method of the IPolicyEnabledBindingElement interface. The ExtendedServiceFactory.CreateServiceHost method calls the ConfigureMessageDescription method for every message that a service endpoint can process. In this method, you can modify the message description that occurs. You can leave the body of this method empty if there is no policy defined for this message.
- This method takes two parameters: a System.ServiceModel.Description.MessageDescription object that represents the fault and a Microsoft.ConnectedIndustry.ServiceModel.Policy.PolicyAssertion object that represents the custom policy.
- Implement the ValidatePolicies method of the IPolicyEnabledBindingElement interface. The ValidatePolicies method takes a Microsoft.ConnectedIndustry.ServiceModel.Policy.ServicePolicies object as a parameter. The Microsoft.ConnectedIndustry.ServiceModel.Policy.ServicePolicies object contains a list of the service-level and operation-level policies for the service endpoint. It returns a Boolean value that indicates whether validation was successful.
- The ValidatePolicies method can examine the listed policies and verify that they are configured correctly. If the policy configurations are correct, ValidatePolicies returns true. If the configurations are not correct, it returns false.
- Override the PolicyPrefix property inherited from the BaseBindingElement class. This is a read-only string property. The prefix is used with the policy name and namespace to identify the policy.
- Override the PolicyName property inherited from the BaseBindingElement class. This is a read-only string property. The policy name should uniquely identify the policy in the namespace specified for the policy.
- Override the PolicyNamespace property inherited from the BaseBindingElement class. This is a read-only string property. The policy namespace is combined with the policy name to uniquely identify the policy.
- Override the GetCurrentBindingElement method inherited from the BaseBindingElement class. This method takes a Microsoft.ConnectedIndustry.ServiceModel.Policy.ServicePolicies object as a parameter. The object contains information about the service and operation-level policies defined for the service endpoint. This method should return an instance of the custom binding element class.
Note: |
|---|
| You can also override the following methods if you need to customize the behavior of a binding element or extend it beyond the default implementation provided in the BaseBindingElement class: BuildChannelFactory<TChannel>, BuildChannelListener<TChannel>, InternalExportPolicy, OnGetPolicyAssertion, HandleIncomingMessageDescription, GetPolicyIdentifier, CanBuildChannelFactory, CanBuildChannelListener. For more information, see The BaseBindingElement Class. |
The following code example shows a custom binding element class named SampleBindingElement. This binding element class provides the binding element for the SamplePolicy custom policy.
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Diagnostics;
using Microsoft.ConnectedIndustry.ServiceModel.ProtocolChannels.Channels;
using Microsoft.ConnectedIndustry.ServiceModel.Policy;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using Microsoft.ConnectedIndustry.ServiceModel.Common;
namespace DCS.Samples.SamplePolicy
{
public class SampleBindingElement : Microsoft.ConnectedIndustry.ServiceModel.ProtocolChannels.Channels.BaseBindingElement, Microsoft.ConnectedIndustry.ServiceModel.Policy.IPolicyEnabledBindingElement
{
private SamplePolicy policy = null;
internal const string ModuleName = "SampleBindingElement";
#region Properties
internal SamplePolicy SamplePolicy
{
get
{
return policy;
}
set
{
policy = value;
}
}
#endregion Properties
#region Constructor
public SampleBindingElement()
: this(new ServicePolicies(), false)
{
// To Do: give implementation as required
}
public SampleBindingElement(ServicePolicies policies)
: this(policies, false)
{
// To Do: give implementation as required
}
public SampleBindingElement(bool mustActivate)
: this(new ServicePolicies(), mustActivate)
{
// To Do: give implementation as required
}
public SampleBindingElement(ServicePolicies policies, bool mustActivate)
: base(policies, mustActivate)
{
// To Do: give implementation as required
this.channel = new SampleChannelFilter();
}
#endregion
#region BaseBindingElement Overrides
#region overridden properties
protected override string PolicyPrefix
{
get
{
return "dcs";
}
}
public override string PolicyName
{
get
{
return SamplePolicy.samplePolicyName;
}
}
public override string PolicyNamespace
{
get
{
return Namespaces.PolicyNamespace;
}
}
#endregion overridden properties
public override BindingElement Clone()
{
SampleBindingElement clone = new SampleBindingElement();
clone.SamplePolicy = this.SamplePolicy;
return clone;
}
protected override BaseBindingElement GetCurrentBindingElement(ServicePolicies policies)
{
Tracer.Write(TraceEventType.Verbose, "SampleBindingElement", "Entering ProcessOutgoingRequest");
return new SampleBindingElement(policies);
}
protected override void InternalExportPolicy(MetadataExporter exporter, PolicyConversionContext context)
{
Tracer.Write(TraceEventType.Verbose, ModuleName, "Entering SamplePolicy");
base.InternalExportPolicy(exporter, context);
if (policy == null)
{
Tracer.Write(TraceEventType.Error, ModuleName, "SamplePolicy: there is no sample policy for this service");
return;
}
string policyAssertion = policy.CreateAssertion();
XmlDocument policyDocument = new XmlDocument();
policyDocument.LoadXml(policyAssertion);
XmlElement assertionPolicyFragment = policyDocument.DocumentElement;
XmlDocument parentDocument = new XmlDocument();
XmlElement policyAssertionElement = parentDocument.CreateElement(SamplePolicy.samplePolicyPrefix, SamplePolicy.samplePolicyName, SamplePolicy.samplePolicyNamespace);
XmlElement encodingAssertionElement = policyDocument.DocumentElement;
foreach (XmlAttribute childAttribute in assertionPolicyFragment.Attributes)
{
XmlAttribute newAttribute = parentDocument.CreateAttribute(childAttribute.Prefix, childAttribute.LocalName, childAttribute.NamespaceURI);
newAttribute.Value = childAttribute.Value;
policyAssertionElement.Attributes.Append(newAttribute);
}
foreach (XmlNode childNode in assertionPolicyFragment.ChildNodes)
{
policyAssertionElement.AppendChild(parentDocument.ImportNode(childNode, true));
}
Tracer.Write(TraceEventType.Verbose, ModuleName, "SamplePolicy: created the assertion for the SamplePolicy");
// add assertion to exported policies
context.GetBindingAssertions().Add(policyAssertionElement);
}
#endregion
#region IPolicyEnabledBindingElement Members
public void ConfigureEndpointDescription(System.ServiceModel.Description.ServiceEndpoint endpointDescription, ServicePolicies servicePolicies)
{
string definition = SamplePolicy.InternalPolicyDefinition;
SamplePolicy samplePolicy;
if (servicePolicies.Policies.ContainsKey(definition))
{
Tracer.Write(TraceEventType.Verbose, ModuleName, "Found sample policy with assertion : {0}", definition);
samplePolicy = SamplePolicy.TryDecodeAssertion(servicePolicies.Policies[definition].PolicyAssertion.Assertion.OuterXml);
if (samplePolicy != null)
{
Tracer.Write(TraceEventType.Verbose, ModuleName, "Decoded sample policy assertion", definition);
CustomBinding newBinding = new CustomBinding(endpointDescription.Binding);
// pass samplePolicy to sampleBindingElement so it can export it
SampleBindingElement bindingElement = newBinding.Elements.Find<SampleBindingElement>();
bindingElement.SamplePolicy = samplePolicy;
// change the binding for this service endpoint with the new one
endpointDescription.Binding = newBinding;
}
}
}
public void ConfigureFaultDescription(
FaultDescription faultDescription, PolicyAssertion assertion)
{
Tracer.Write(TraceEventType.Verbose, "SampleBindingElement", "Entering ConfigureFaultDescription");
// To Do: give implementation as required
}
public void ConfigureMessageDescription(
MessageDescription messageDescription, PolicyAssertion assertion)
{
Tracer.Write(TraceEventType.Verbose, "SampleBindingElement", "Entering ConfigureMessageDescription");
// To Do: give implementation as required
}
public bool ValidatePolicies(ServicePolicies servicePolicies)
{
Tracer.Write(TraceEventType.Verbose, "SampleBindingElement", "Entering ValidatePolicies");
return true;
}
#endregion
}
}
The next code example shows the ContextBindingElement class for the ContextPolicy policy implemented by DCS. This class references the ContextEndpointBehavior and ContextParameterInspector classes, which are also shown in the example.
public class ContextBindingElement : BaseBindingElement, IPolicyEnabledBindingElement
{
// Fields
internal const string ModuleName = "ContextBindingElement";
// Methods
public ContextBindingElement() : this(new ServicePolicies(), false)
{
}
public ContextBindingElement(ServicePolicies policies) : this(policies, false)
{
}
public ContextBindingElement(bool mustActivate) : this(new ServicePolicies(), mustActivate)
{
}
public ContextBindingElement(ServicePolicies policies, bool mustActivate) : base(policies, mustActivate)
{
base.channel = new ContextChannelFilter();
}
public override BindingElement Clone()
{
return new ContextBindingElement(base.policies, base.mustActivate);
}
private static void EnsureContextBehavior(ServiceEndpoint endpointDescription)
{
if (!endpointDescription.Behaviors.Contains(typeof(ContextEndpointBehavior)))
{
endpointDescription.Behaviors.Add(new ContextEndpointBehavior());
}
}
protected override BaseBindingElement GetCurrentBindingElement(ServicePolicies policies)
{
return new ContextBindingElement(policies);
}
protected override void HandleIncomingMessageDescription(MessageDescription currentMessageDescription)
{
XmlQualifiedName contextHeader = new XmlQualifiedName("context", "http://schemas.Microsoft.com/ConnectedIndustry/ServiceModel/2008/05/Context");
if (currentMessageDescription.Headers.Contains(contextHeader))
{
MessageHeaderDescription headerDescription = currentMessageDescription.Headers[contextHeader];
headerDescription.Type = ContextFactory.ContextType;
}
}
void IPolicyEnabledBindingElement.ConfigureEndpointDescription(ServiceEndpoint endpointDescription, ServicePolicies servicePolicies)
{
EnsureContextBehavior(endpointDescription);
}
void IPolicyEnabledBindingElement.ConfigureFaultDescription(FaultDescription faultDescription, PolicyAssertion assertion)
{
}
void IPolicyEnabledBindingElement.ConfigureMessageDescription(MessageDescription messageDescription, PolicyAssertion assertion)
{
if (assertion != null)
{
ProtectionPolicy protectionPolicy = null;
foreach (SinglePolicyAssertion singleAssertion in assertion.PolicyAssertions)
{
if (singleAssertion.PolicyAssertionId == ProtectionPolicy.protectPolicyDefinition)
{
protectionPolicy = BasePolicy<ProtectionPolicy>.TryDecodeAssertion(singleAssertion.Assertion.OuterXml);
break;
}
}
if ((protectionPolicy != null) && (protectionPolicy.ProtectionLevel != ProtectionLevel.None))
{
Tracer.Write(TraceEventType.Verbose, "ContextBindingElement", "In Configure Message Description, Protection Level = {0}", new object[] { protectionPolicy.ProtectionLevel });
bool contextEnabled = false;
foreach (SinglePolicyAssertion singleAssertion in assertion.PolicyAssertions)
{
if (singleAssertion.PolicyAssertionId == ContextPolicy.InternalPolicyDefinition)
{
ContextPolicy contextPolicy = BasePolicy<ContextPolicy>.TryDecodeAssertion(singleAssertion.Assertion.OuterXml);
if ((contextPolicy != null) && contextPolicy.RequireContext)
{
contextEnabled = true;
break;
}
}
}
if (contextEnabled)
{
XmlQualifiedName contextHeader = new XmlQualifiedName("context", "http://schemas.Microsoft.com/ConnectedIndustry/ServiceModel/2008/05/Context");
if (!messageDescription.Headers.Contains(contextHeader))
{
MessageHeaderDescription header = new MessageHeaderDescription("context", "http://schemas.Microsoft.com/ConnectedIndustry/ServiceModel/2008/05/Context");
header.Type = ContextFactory.ContextType;
messageDescription.Headers.Add(header);
}
messageDescription.Headers[contextHeader].ProtectionLevel = protectionPolicy.ProtectionLevel;
}
}
}
}
bool IPolicyEnabledBindingElement.ValidatePolicies(ServicePolicies servicePolicies)
{
return true;
}
// Properties
public bool Enable
{
get
{
return base.mustActivate;
}
set
{
base.mustActivate = value;
}
}
public override string PolicyName
{
get
{
return "contextPolicy";
}
}
public override string PolicyNamespace
{
get
{
return "http://schemas.Microsoft.com/ConnectedIndustry/ServiceModel/2008/05/Policy";
}
}
protected override string PolicyPrefix
{
get
{
return "dcs";
}
}
}
public class ContextEndpointBehavior : IEndpointBehavior
{
// Methods
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
foreach (DispatchOperation operation in endpointDispatcher.DispatchRuntime.Operations)
{
operation.ParameterInspectors.Add(new ContextParameterInspector());
}
Tracer.Write(TraceEventType.Verbose, "ContextBindingElement", "ContextEndpointBehavior added to the current endpoint");
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
public class ContextParameterInspector : IParameterInspector
{
// Methods
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
{
}
public object BeforeCall(string operationName, object[] inputs)
{
if ((OperationContext.Current != null) && OperationContext.Current.IncomingMessageProperties.ContainsKey("http://schemas.Microsoft.com/ConnectedIndustry/ServiceModel/2008/05/Context"))
{
Tracer.Write(TraceEventType.Verbose, "ContextBindingElement", "ContextParameterInspector: context setted in the current thread");
Context.Current = OperationContext.Current.IncomingMessageProperties["http://schemas.Microsoft.com/ConnectedIndustry/ServiceModel/2008/05/Context"] as Context;
}
return null;
}
}
- Create a new class that inherits from the System.ServiceModel.Configuration.BindingElementExtensionElement class. For more information, see The BindingElementExtensionElement Class.
- Override the CreateBindingElement method and construct an instance of the custom binding element that supports the custom policy. Return this instance as a BindingElement object.
- Override the Type read-only property. Implement a get accessor that returns a BindingElementType object that contains the type of the custom binding element.
- Implement properties to enable and disable each of the properties exposed by the custom policy class. Decorate each property with the ConfigurationProperty attribute so that they can be accessed in a client application or a service configuration file. Specify the name of the property as it appears in the configuration file, the default value for the property if the user does not specify it in the configuration file, and the IsRequired flag to indicate whether this property is mandatory or optional. For more information, see The ConfigurationPropertyAttribute Class.
Note:The BindingElementExtensionElement class implements an indexer that you can use to read and write the setting for a custom property.
The following code example shows a custom binding element extension class named SampleBindingElementExtension. This binding element class provides the binding element extension for the SamplePolicy custom policy.
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.Configuration;
using System.Diagnostics;
namespace DCS.Samples.SamplePolicy
{
public class SampleBindingElementExtension : BindingElementExtensionElement
{
[ConfigurationProperty("enable", DefaultValue = true, IsRequired = false)]
public bool Enable
{
get
{
return (bool)base["enable"];
}
set
{
base["enable"] = value;
}
}
public override Type BindingElementType
{
get { return typeof(SampleBindingElement); }
}
protected override BindingElement CreateBindingElement()
{
SampleBindingElement element = null;
if (Enable)
{
element = new SampleBindingElement();
}
return element;
}
}
}
The next code example shows the ContextElementExtension class for the ContextPolicy policy implemented by DCS.
public class ContextExtensionElement : BindingElementExtensionElement
{
// Methods
protected override BindingElement CreateBindingElement()
{
Trace.WriteLine("ContextExtensionElement::CreateBindingElement");
ContextBindingElement element = new ContextBindingElement();
element.Enable = this.Enable;
return element;
}
// Properties
public override Type BindingElementType
{
get
{
return typeof(ContextBindingElement);
}
}
[ConfigurationProperty("enable", DefaultValue=false, IsRequired=false)]
public bool Enable
{
get
{
return (bool) base["enable"];
}
set
{
base["enable"] = value;
}
}
}