Tom Hollander's blog

patterns, practices and pontification

Configuring the Policy Injection Application Block

Configuring the Policy Injection Application Block

  • Comments 16

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.

  • Hey Tom, given that the implementation for the Policy block somewhat goes hand-in-hand with the business objects exposed to the UI, I have found (ever since 2003) that it was impossible to use design-time support for objects that implement from MarshalByRefObject.

    I know that talking to you I can create implementations of business objects w/o inheriting from MarshalByRef.

    To-my-knowledge, you can't take this approach when making business objects that support design time interaction (drag-and-drop a business object in the VS Toolbar onto a windows form design surface of VS2005 [bizObject implements IComponent and ToolBarVisible attribute).

    I know 2005 has a BindingSource component, and maybe there is a solution using that option.

    But could there be guidance (or a sample) that shows the use of data binding when using Policies?

    Say validating that a public property like 'SSN' must match a regex defined by a policy, and then using IDataError to automatically show this error to the user?

    Conceptually I can see how it could be done with the Policy injection, and I see great benefit in that UI Developers don’t care / know about rules, they are merely concerned with UI elements. The one stop-gap I see is design-time support for Policy Injection.

    Any Guidance would be appreciated on top of the spot-on-job you guys are doing with Ent Lib :)

  • 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=397

  • Thanks for this sample, i love it!

  • 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 configuration

  • Since Both Tom and Ed wrote an article explaining whatever the Policy Injection Application Block [PIAB]

  • I am busy with a few things in the moment so expect this, hopefully, daily entries to improve dramatically

  • The next version of Enterprise Library will be getting an AOPish block called the Policy Injection Application...

  • How to hook validation against Grid Columns?

  • Yeh....!! I thought so about it. But no way around it

  • 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?

  • DaeWonRyu - did you reference the CallHandlers and Logging assemblies from your project?

    Tom

  • 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.

  • 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?

  • 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?

  • 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....

Page 1 of 2 (16 items) 12