The Patterns & Practices team at Microsoft recently released the 3.0 version of the ever-useful Enterprise Library. Among the changes in the current iteration of the library is the addition of a new application block - the Policy Injection Application Block ("PIAB").
The purpose of the PIAB is to provide developers a way to define "policies" that can be applied selectively throughout an application. Imagine that you're building a human resources application and one of the requirements is that the application provide an audit trail for all changes made to an employee's personnel record. The PIAB allows you to define this requirement as a "policy" and configure its behavior external to your application.
Here's how it works:
- Using the Enterprise Library Configuration tool, you define a policy (in this case we'll name it "Audit Policy")
- Each policy can have any number of associated "matching rules"; these rules define how/when the policy is applied. The PIAB provides the following rules out-of-the-box:
- Assembly Matching Rule
- Custom Attribute Matching Rule
- Member Name Matching Rule
- Method Signature Matching Rule
- Namespace Matching Rule
- Parameter Type Matching Rule
- Property Matching Rule
- Return Type Matching Rule
- Tag Attribute Matching Rule
- Type Matching Rule
- Each policy can also have any number of "handlers"; handlers determine what happens when a policy is executed. The PIAB provides the following handlers, which utilize other Enterprise Library application blocks, out-of-the-box:
- Authorization Handler
- Caching Handler
- Exception Handling Handler
- Logging Handler
- Performance Counters Handler
- Validation Handler
- In our HR application example, we'll elect to use a "Tag Attribute Matching Rule" that allows us to mark methods in our business tier classes as requiring audit functionality; in addition, we'll choose to apply the Enterprise Library's Logging Application Block as our policy's handler.
The resulting XML in our application's configuration file looks like this (the Logging Application Block section has been omitted for brevity):
<policyInjection>
<policies>
<add name="AuditPolicy">
<matchingRules>
<add match="Audit" ignoreCase="false"
type=
"Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.TagAttributeMatchingRule,
Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=3.0.0.0,
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
name="Tag Attribute Matching Rule" />
</matchingRules>
<handlers>
<add logBehavior="After" beforeMessage="" afterMessage="" eventId="0"
includeParameterValues="true" includeCallStack="false" includeCallTime="true"
priority="-1" severity="Information"
type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers.LogCallHandler,
Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers,
Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
name="Logging Handler">
<categories>
<add name="General" />
</categories>
</add>
</handlers>
</add>
</policies>
</policyInjection>
- In our application, we're going to mark each method that requires an audit trail with a "Tag" attribute...notice that "Audit" corresponds to our matching rule defined above:
/// <summary>
/// Note: class must inherit MarshalByRefObject
/// for use with the PIAB
/// </summary>
class Employee : MarshalByRefObject
{
[Tag("Audit")]
public void Save()
{
//write employee to data store
}
[Tag("Audit")]
public void Delete()
{
//remove employee from data store
}
}
- The "policy injection magic" performed by the PIAB will occur as a result of the way we create an instance of our Employee class:
Employee ourEmployee = PolicyInjection.Create<Employee>();
ourEmployee.Save();
The Create() factory method actually builds and returns a "proxy class" -- in this example, the proxy class intercepts our call to the Save() method, evaluates the matching criteria and determines that the handler should be executed because of the "Audit" tag match. The result in this case is that an entry is written to the Windows event log as defined by our Logging Application Block configuration.
The "policy injection" pattern is worth a look if you have common functionality that needs to be applied throughout your application, and you want to do so in a declarative, transparent way.