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:

  1. 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.
  2. 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.
  3. 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.