Since Both Tom and Ed wrote an article explaining whatever the Policy Injection Application Block [PIAB] is, you might be interested in how to use this new block from within Enterprise Libraries February CTP. Tom, who is the Product Manager on Enterprise Library, has been keeping me fairly busy over the last couple of days and helped out posting some of the OPAB Configuration schema already.
So let’s start with this exercise in using the Policy Injection Application Block!
Since I wouldn’t know about your real world application (though feel free to post specific problems you run into on the forums!) I created my own “real world” application. An ATM (or Automated Teller Machine):
As most other real world application this application is about more than just decreasing the balance after a withdrawal is made. The requirements include:
- Log all operations to a text file.
- Log all withdrawals of $1000 or above to the eventlog (the business guys refer to these withdrawals as “Large Withdrawal”).
- No withdrawal can be made that exceeds the customer’s credit limit
- Exceptions that occur, should be handled gracefully
Now, let’s dive into the code and have a look at how this Policy Injection thing will help us out.
(The complete project can be found as an attachment, the complete listing of PIAB configuration can be found at the bottom of this blogpost)
Defining the “Interceptable” interface
Since we will be intercepting method calls within the application we first need to determine what methods we would be potentially interested in to intercept. This can be done in 2 ways:
1) If we want to have the system intercept *all* calls to a specific class (and we have the freedom of defining our own base class on this class) we can do so by having this class derive from MarshalByRefObject.
2) If we want a more fine grained interface to do interception on (or we already have a baseclass, other than MarshalByRefObject) we can have our class implement an interface, which will define the methods we can do interception on.
Additionally, all classes that have methods we would like to intercept need to be created using a factory, called PolicyInjection. In code this looks like:
//All methods on this class can be intercepted, it derives from MarshalByRefObject
public class InterceptAllMyMethods : MarshalByRefObject
{
public void Method1() { }
public void Method2() { }
}
(then, when creating an instance of the class above)
//Have the factory create the instance, this will make interception possible
InterceptAllMyMethods interceptable1 = PolicyInjection.Create<InterceptAllMyMethods>();
interceptable1.Method1(); //this method call will be intercepted by the PolicyInjection AB
or:
//Methods defined on IInterceptableMethods can be intercepted
public class InterceptMethodsInInterface : IInterceptableMethods
{
public void Method1() { }
public void Method2() { } //not in interface, will not be intercepted
}
public interface IInterceptableMethods
{
void Method1();
}
(again, create the object using the factory)
IInterceptableMethods interceptable2 = PolicyInjection.Create<InterceptMethodsInInterface, IInterceptableMethods>();
interceptable2.Method1(); //this method call will be intercepted by the PolicyInjection AB
In our teller machine we choose the second option, and defined an interface that looks like the following:
public interface ITeller
{
void WithdrawAmount20(Account account);
void WithdrawAmount50(Account account);
void WithdrawAmount100(Account account);
void WithdrawAmount500(Account account);
[Tag("Large Withdrawal")]
void WithdrawAmount1000(Account account);
[Tag("Large Withdrawal")]
void WithdrawAmount2500(Account account);
void Kicked();
}
When creating an instance of the Teller class (which derived from this interface) we do this using the PolcyInjection factory. This will make every method call in the interface about a possible victom for interception.
teller = PolicyInjection.Create<Teller, ITeller>();
Now this is done, we can proceed with our first requirement:
Logging every operation to a text file
Since we have our instance of the Teller class created using the PolicyInjection factory, next thing we need to think about is what to do in order to satisfy this requirement.
Every operation in this requirement can be interpreted as “all method calls to the ITeller interface” (above). Logging this to a textfile, in our context can be interpreted as use Enterprise Library logging to write the event to a text file.
These 2 bits of information (the where and the what) make up a Policy. This Policy can be configured and will be Injected into the Teller class (without the need of explicitly writing the code to call The Logging Application Block, over and over again).
The generic skeleton for a policy in configuration looks like the following:
<policies>
<add name="PolicyName">
<matchingRules>
[List of rules that should evelautate true,
before policy is applied]
</matchingRules>
<handlers>
[List of handlers that should be invoced
by this Policy]
</handlers>
</add>
</policies>
For this specific policy we add:
1) A rule to the matchingRule-element that will match any method on the ITeller interface
2) A handler that calls into the Logging Application block, to update the textfile, we use as a log.
In the applications configuration this now looks like the following:
<add name="LogAllOperationsToFile">
<matchingRules>
<add
name="Type is ITeller"
type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.TypeMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection"
ignoreCase="false"
match="ITeller" />
</matchingRules>
<handlers>
<add