Configuring the Policy Injection Application Block
In my last post I promised you a bit of help in evaluating the preview of the Policy Injection Application Block which is in the February 2007 CTP of Enterprise Library 3.0. I know Olaf is working on a tutorial (and knowing David I'll bet he's cooking up something too), but I wanted to give you a few brief pointers in the meantime. In particular, the current implementation relies heavily on configuration, but without support from the configuration tool or any samples, it's pretty hard to figure out what the configuration is meant to look like.
To help with this, I'll share a few parts of the QuickStart sample I'm working on (before our version of the code diverges too much from what works in the CTP!). In the sample I build a very simple "business logic" class as follows.
namespace PolicyInjectionQuickStart.BusinessLogic
{
public class BankAccount : MarshalByRefObject
{
private decimal balance;
public decimal GetCurrentBalance()
{
return balance;
}
[Tag("ValidateMe")]
public void Deposit([RangeValidator(0, RangeBoundaryType.Exclusive, 0, RangeBoundaryType.Ignore)] decimal depositAmount)
{
balance += depositAmount;
}
[Tag("ValidateMe")]
public void Withdraw([RangeValidator(0, RangeBoundaryType.Exclusive, 1000, RangeBoundaryType.Inclusive)] decimal withdrawAmount)
{
if (withdrawAmount > balance)
{
throw new InvalidOperationException();
}
balance -= withdrawAmount;
}
}
}
Most of this code is completely trivial. The only things that look unusual are the attributes. As you'll soon see, the Tag attribute is one way you can apply policies to particular members, by building a policy with the TagAttributeMatchingRule. The validation attributes, as you can probably guess, are there to provide validation metadata for the Validation handler. Unfortunately the version of the handler in the February 2007 CTP isn't yet able to inspect for these attributes, so this code won't do anything yet (the current implementation only checks validation rules defined in the types or in configuration). But since this code will work eventually, I put it in the QuickStart anyway.
I won't bother sharing the application that uses this class, but it's pretty much exactly what you'd expect. The only thing with noting out is that the instance of BankAccount is created using the PIAB's PolicyInjection factory;
BusinessLogic.BankAccount bankAccount =
PolicyInjection.Create<BusinessLogic.BankAccount>();
...and from then on, methods are just called in the old fashioned way. But to make everything come together, you'll need to configure the block. Here are the policies I defined. Note that the application also hosts several other application blocks, including Logging, Exception Handling, Validation and Security, but I've left out their configuration for (relative) brevity.
<configuration>
<configSections>
<section name="policyInjection" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.Configuration.PolicyInjectionSettings, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=2.9.9.2, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</configSections>
<policyInjection>
<policies>
<add name="Authorize and Audit">
<matchingRules>
<add type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.NamespaceMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=2.9.9.2, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" name="Namespace Matching Rule" match="PolicyInjectionQuickStart.BusinessLogic" ignoreCase="false" />
</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="BeforeAndAfter" beforeMessage="Before" afterMessage="After" includeParameterValues="true" includeCallTime="true" includeCallStack="false" severity="Information">
<categories>
<add name="Audit" />
</categories>
</add>
<!--Not supported in the Feb CTP-->
<!--add name="AuthZ Handler" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers.AuthorizationCallHandler, Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers, Version=2.9.9.2, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
operationName="{method}" authorizationProvider="RuleProvider"/-->
</handlers>
</add>
<add name="Exception Handling">
<matchingRules>
<add type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.TypeMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=2.9.9.2, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" name="Type Matching Rule" match="BankAccount" ignoreCase="false" />
<add type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.MemberNameMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=2.9.9.2, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" name="Member Matching Rule" match="Withdraw" ignoreCase="false" />
</matchingRules>
<handlers>
<add name="Exception Handler" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers.ExceptionCallHandler, Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers, Version=2.9.9.2, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" exceptionPolicyName="Bank Account Policy" />
</handlers>
</add>
<add name="Validation">
<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="ValidateMe" ignoreCase="true" />
</matchingRules>
<handlers>
<add name="Validation Handler" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers.ValidationCallHandler, Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers, Version=2.9.9.2, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" specificationSource="Both" />
</handlers>
</add>
</policies>
</policyInjection>
</configuration>
While the XML isn't quite as pretty as the node hierarchy you'll eventually see in the configuration tool, hopefully it's pretty self explanatory. I've defined three different policies here:
- The Audit and Authorize policy applies to every member of every PolicyInjectionQuickStart.BusinessLogic namespace, and applies a logging handler and (ultimately) an authorization handler. The logging handler will log events before and after the method is called, and will included detailed information including the parameter values. The authorization handler will eventually check access on the method, using the method's name as the operation.
- The Exception Handling policy applies only to the Withdraw method on the BankAccount class. It will apply the Exception Handling Handler on that method, which means any exceptions coming out will go through the Exception Handling Application Block using the Bank Account Policy.
- The Validation policy applies on any type with the Tag attribute specified with the "ValidateMe" tag. It includes just the Validation handler.
Note that it's possible for several policies to apply to the same member, and as such ordering of the policies (as well as ordering of the handlers) is significant. In the case of this example, the following policies and handlers will apply to each method in my class:
- GetCurrentBalance:
- Audit and Authorize (Logging, Authorization)
- Deposit:
- Audit and Authorize (Logging, Authorization)
- Validation (Validation)
- Withdraw:
- Audit and Authorize (Logging, Authorization)
- Exception Handling (Exception Handling)
- Validation (Validation)
I hope this gives you a few useful tips that you can use to start exploring this block on your own. As always, please keep your feedback coming by posting to the CodePlex discussions.
Comments
Anonymous
March 02, 2007
The comment has been removedAnonymous
March 02, 2007
Cool stuff.. I really like the way of using the PAIB and VB Attributes to specify validations on methods, it can be used for some kind of "Design by contract". I wrote about that on my blog: http://fredrik.nsquared2.com/viewpost.aspx?PostID=397Anonymous
March 03, 2007
Thanks for this sample, i love it!Anonymous
March 03, 2007
The Policy Injection Application Block in Enterprise Library 3.0 can save you from having to write all those boring validation, security, exception handling, and security-related infrastructure code and instead allow you to write policies in your configurationAnonymous
March 04, 2007
Since Both Tom and Ed wrote an article explaining whatever the Policy Injection Application Block [PIAB]Anonymous
March 05, 2007
I am busy with a few things in the moment so expect this, hopefully, daily entries to improve dramaticallyAnonymous
March 05, 2007
The next version of Enterprise Library will be getting an AOPish block called the Policy Injection Application...Anonymous
March 12, 2007
How to hook validation against Grid Columns?Anonymous
March 12, 2007
Yeh....!! I thought so about it. But no way around itAnonymous
March 12, 2007
well, It's cool...but when I ran the application, I got an Exception like this. "The type 'Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers.LogCallHandler, Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers, Version=2.9.9.2, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' cannot be resolved. Please verify the spelling is correct or that the full type name is provided." Did I miss something?Anonymous
March 13, 2007
DaeWonRyu - did you reference the CallHandlers and Logging assemblies from your project? TomAnonymous
March 13, 2007
Thanks Tom. I referenced the Microsoft.Pratices.EnterpriseLibrary.PolicyInjection.dll (I thought that assembly included CallHandlers..I missed --;) I referenced Microsoft.Pratices.EnterpriseLibrary.PolicyInjection.CallHandlers assembly and it works very well. I hoped MS might support AOP, and It did. Great job.Anonymous
March 15, 2007
Thanks, I also like the "Design by contract" way of doing this. But could there be more. So FI in the Withdraw method validation specify balance instead of 1000? And could/may we use this for check on null parameters?Anonymous
April 17, 2007
the CSLA by Lhotka allows to apply business rules to windows and web business objects and to separate UI, Business, and Data layers keeping in mind that business objects cannot be actually created but either loaded from DB or derived from some base class to enforce validation and security for both the business and the data layers, besides logging and the runtime configuration, what are the diferences of PIAB and CSLA and can both work together?Anonymous
April 25, 2007
I know this post is intended to provide an example of the new policy injection features (great example and thank you) But does the RangeValidatorAttribute work for decimal types? or am I going mad....Anonymous
April 26, 2007
Tom - yes it does, but not with the syntax in the blog (apologies for this!). It turns out you cannot use decimal literals in attributes in C#, so you need to specify them as strings, ie: [RangeValidator(typeof(Decimal), "0.0", RangeBoundaryType.Exclusive, "0.0", RangeBoundaryType.Ignore)] A working version of this code is included in the PIAB QuickStart. Tom