Policy Injection Application Block Example
by David Hayden ( Microsoft MVP C# ), Filed: Enterprise Library 3.0
As I mentioned earlier, the February 2007 CTP of Enterprise Library 3.0 was released on Feb 28 and it introduces a new application block, the Policy Injection Application Block ( PIAB ).
As I understand it, the Policy Injection Application Block introduces AOP-like functionality into your winform and web applications by intercepting normal method calls and running handlers before and after method execution as defined by you through configuration. It does this by handing you a proxy class during factory creation that represents the class you asked for:
IOrder order =
PolicyInjection.Create<Order, IOrder>();
If there are policies defined in the configuration file that specify certain methods be intercepted on the Order Class, the statement above will return a proxy class that does method interception, otherwise it returns an instance of the Order Class.
The policy configuration information can be located in any configuration file and looks like this:
<policyInjection>
<policies>
<add name="Policy1">
<matchingRules>
<add name="MatchingRule1" ... />
<add name="MatchingRule2" ... />
</matchingRules>
<handlers>
<add name="Handler1" ... />
<add name="Handler2" ... />
</handlers>
</add>
</policies>
</policyInjection>
Matching Rules tell the Policy Injection Application Block what methods to intercept and the handlers specify what happens before and / or after interception.
Policy Injection Application Block Example
Let's say we have a policy for our store that whenever an order is returned it must be logged along with the reason it was returned. For simplification, here are my classes:
public class Order : IOrder
{
public void Return(string reason)
{
// Do something...
}
}
public interface IOrder
{
void Return(string reason);
}
One option is to use the Enterprise Library Logging Application Block directly and somewhere during the Return Process just make a call that logs the information:
LogEntry logEntry = new LogEntry();
logEntry.Message = "..."
logEntry.Categories.Add("...");
// ...
Logger.Write(logEntry);
With the Policy Injection Application Block, however, we can avoid this extra code by defining a policy that whenever a call is made to the Return Method on IOrder we should log a message.
There are numerous ways to match policies to methods, but in this case I am going to use the TagMatchingRule which means I just have to add a tag attribute to the Return Method on the Interface. Hence rather than writing all the LogEntry code above and explicitly calling the logging application block, I will just add the following to the IOrder Interface:
public interface IOrder
{
[Tag("Log")]
void Return(string reason);
}
I then define a Policy in my configuration file that says whenever [Tag("Log")] is on a method in my application ( as defined via my IOrder interface ), intercept the call and log information to the logging provider defined by the Logging Application Block:
<policyInjection>
<policies>
<add name="Logging">
<matchingRules>
<add type="Microsoft.Practices.
EnterpriseLibrary.PolicyInjection.MatchingRules.
TagAttributeMatchingRule,
Microsoft.Practices.
EnterpriseLibrary.PolicyInjection,
Version=2.9.9.2, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a"
name="Tag Matching Rule"
match="Log" ignoreCase="true" />
</matchingRules>
<handlers>
<add name="Logging Handler"
type="Microsoft.Practices.EnterpriseLibrary.
PolicyInjection.CallHandlers.LogCallHandler,
Microsoft.Practices.
EnterpriseLibrary.PolicyInjection.CallHandlers,
Version=2.9.9.2, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a"
logBehavior="Before"
beforeMessage="Before" includeParameterValues="true"
includeCallTime="true" includeCallStack="false"
severity="Information">
<categories>
<add name="General" />
</categories>
</add>
</handlers>
</add>
</policies>
</policyInjection>
There is a decent amount of configuration in there, but you won't normally have to look at it because you will be using the Graphical Configuration Tool that is integrated into Visual Studio for managing your Enterprise Library configurations.
So when the following happens in my application:
IOrder order =
PolicyInjection.Create<Order, IOrder>();
order.Return("No change in size.");
The Policy Injection Application Block substitutes a proxy class for my Order Class because a policy is defined on the IOrder Interface: [Tag("Log")].
When the Return Method is called, the proxy class intercepts the call and runs the handlers in the order as defined in my configuration before and after calling the Return Method associated with the Order Class. In this case, it runs the LogCallHandler which logs information in the EventLog as I defined in the Logging Application Block Section ( but didn't show here for brevity ). What ends up in my EventLog is similar to the following but would realistically provide more useful information and be logged somewhere else:
Timestamp: 3/5/2007 9:12:17 PM
Message: Before
Category: General
Priority: 0
EventId: 1
Severity: Information
Extended Properties: reason - No change in size.
Again, the code to do this logging is defined in policy and matched by an attribute. Here is all the code:
namespace LogMe
{
class Program
{
static void Main(string[] args)
{
IOrder order =
PolicyInjection.Create<Order, IOrder>();
order.Return("No change in size.");
}
}
public class Order : IOrder
{
public void Return(string reason)
{
// Do something...
}
}
public interface IOrder
{
[Tag("Log")]
void Return(string reason);
}
}
A restriction worth mentioning is that in order for a method to be intercepted, the class in question must either inherit from MarshalByRefObject or implement an Interface.
As you can see, the Policy Injection Application Block is definitely cool and a nice addition to the Application Blocks.
But Where's My Dependency Injection?
I love the new block, but have a bit of a concern. One of my concerns about the code above is this statement:
IOrder order = PolicyInjection.Create<Order, IOrder>();
Notice that I have to specify the concrete name of the class as well as the interface it implements. I would like to have a Create Method like this:
IOrder order = PolicyInjection.Create<IOrder>();
and via some Dependency Injection Container or better yet, Dependency Injection Application Block ( DIAB ), the Policy Injection Application Block just locate the concrete class ( or service really ) that goes with the interface.
I mentioned this disappointment about no DI Container for use with ObjectBuilder in another post:
ObjectBuilder Dependency Injection Framework - Poor Undocumented Little Soul :)
So now as I mentioned in this CodePlex Discussion in the Enterprise Library Forums:
http://www.codeplex.com/entlib/Thread/View.aspx?ThreadId=7807
I have to use a Dependency Injection Tool alongside the Policy Injection Application Block even though ObjectBuilder plays a huge role in Enterprise Library.
And, of course, most of the software factories, like the Web Client Software Factory that I have been giving presentations on at various Florida Code Camps, use their own containers, too, so I have to coordinate them with the PIAB.
I am hoping if I keep harping on this I will either get what I want or somebody will educate me why Enterprise Library ( or Object Builder ) doesn't come with a Dependency Injection Application Block so all the applications that use Enterprise Library ( including ones built with the software factories ) don't have to use their own version.
The Policy Injection Application Block could then hook into the Dependency Injection Application Block and give us wonderful capabilities.
Conclusion
Putting the Dependency Injection aside, I really love the possibilities of the new Policy Injection Application Block. The Patterns and Practices Team is doing one helluva job on Enterprise Library and all the Software Factories.
Kudos to Tom Hollander, the Project Manager for Enterprise Library. He is very visible and proactive about providing information about what is happening with Enterprise Library in both the forums and his blog.
You can find the source code for this article on my .NET Source Code and Samples Page.
by David Hayden ( Microsoft MVP C# ), Filed: Enterprise Library 3.0