devxlogo

How To Create a Custom Policy Injection Application Block Handler

How To Create a Custom Policy Injection Application Block Handler

don’t work on a Sunday. My boss doesn’t work on a Sunday. Therefore, I don’t see why my business objects should work on a Sunday either. Of course, there will always be someone trying to access my application on a Sunday, but it’s easy to make use of the Policy Injection Application Block and a simple custom call handler to give objects, methods, and properties a well-earned day off.

OK, so maybe this isn’t a fully “real-world” scenario, but it will give you useful guidance in how to make policy injection work for you. As part of Enterprise Library, the Policy Injection Application Block lets you control the behavior of your applications through configuration. The example handler, by default, blocks Sunday access to any individual target method or property. You can specify the methods or properties either through configuration or by using a custom attribute in code. The sample handler optionally allows you to block access on a Saturday as well. Your objects can then have a whole weekend to rest and recover from the previous week’s exertion.

Figure 1 shows the new handler within the Enterprise Library Configuration Console. You can see that it has a property named NotSaturdayEither, which determines whether the handler will block invocation on Saturdays as well as Sundays.

?
Figure 1. Sample Handler in Enterprise Library Configuration Console: The figure shows the process of adding the NeverOnASunday handler to an application’s configuration.

The Policy Injection Application Block, part of Enterprise Library 3.0 from Microsoft’s patterns & practices group, provides powerful yet easy-to-use techniques for applying policies to objects within your applications. This article looks at how you can create custom handlers for the Policy Injection Application Block, and integrate them with the block so they function just like the built-in handlers.

The process of creating a new handler is simple, and you can easily create a new handler attribute for it so that developers can add the handler to methods and properties at design-time as well as through configuration. Creating the classes to integrate the handler with the configuration tools is a little more complex, but does improve the ease of use for administrators and developers. Therefore, the remainder of this article looks in detail at:

  • How the Policy Injection Application Block uses call handlers
  • How to implement the custom NeverOnASunday call handler
  • How to integrate the custom handler with the configuration tools
  • How to implement the NeverOnASunday call handler attribute
  • How you can use the NeverOnASunday handler in ASP.NET applications
Author’s Note: For more information about the Policy Injection Application Block, and its use in ASP.NET applications, see my earlier article “Using the Policy Injection Application Block in ASP.NET.” For technical details about how the block works, see the topic “Design of the Policy Injection Application Block” in the Enterprise Library Help file. For more details about Enterprise Library, see http://www.codeplex.com/entlib/.

Policy Injection Application Block Call Handlers
The Policy Injection Application Block (PIAB) implements a pipeline of call handlers based on attributes that decorate members of target objects, and on sets of Matching Rules defined within the application configuration. The combination of all these is a Policy. When code uses the static Create or Wrap methods of the PolicyInjection factory class, the block discovers which handlers to apply, builds the pipeline, and injects it between the calling code and the target object.

When the client code calls the target object member (method or property), the block intercepts the call and passes it through all of the configured handlers in the order they are defined in the configuration. Each handler can execute code when called from the client or a previous handler in the pipeline (the pre-processing stage), before invoking the next handler. Handlers can also execute code when the following handler returns control to this handler, after the target method or property access completes (the post-processing stage).

Most handlers perform their tasks in the pre-processing stage, although several built-in handlers use both stages. For example, the Caching Handler looks for a cached result in the pre-processing stage and short-circuits processing to return this value if found; otherwise, it uses the post-processing stage to save the returned value into the cache ready for the next call.

Handlers can, if required, abort the process and short-circuit the call to the target object member. In this case, the handler should generate a suitable return message (perhaps containing an appropriate exception), and pass this back to the previous handler?or to the client if this is the first handler in the pipeline.

The General Implementation of a Call Handler
Call handlers follow a basic pattern, described in the following code. You can see that they implement the ICallHandler interface, which defines a method named Invoke. The interface also defines the GetNextHandlerDelegate that the Invoke method receives and uses to invoke the next handler in the pipeline or the target object member. The Invoke method receives an invocation message as an instance of a class that implements the IMethodInvocation interface, and must return an instance of a class that implements the IMethodReturn interface:

   public class CallHandlerOutine : ICallHandler   {         ... constructor(s), properties, and local variable declarations here ...         public IMethodReturn Invoke(IMethodInvocation input,        GetNextHandlerDelegate getNext)     {       ... pre-processing code goes here ...          // decide whether to invoke the next handler, or short-circuit execution       if (some-condition-based-on-parameters-or-environment)       {            // invoke the next handler or the target member         IMethodReturn msg = getNext()(input, getNext);              ... post-processing code goes here ...            // return the message passed back from following handlers to          // the caller          return msg;          }       else       {            // short-circuit the process and return an exception         Exception ex = new Exception("Some Error Message");         // create a ReturnMessage instance from the original invocation object         IMethodReturn msg = input.CreateExceptionMethodReturn(ex)         // return the new message to the caller          return msg;            }     }   }

Notice how the handler can create a return message from the original invocation message. You can use the CreateMethodReturn method of the invocation message to generate a non-error return message, or (as shown in the code above) the CreateExceptionMethodReturn method to generate a return message containing an exception and error message. You will see how to access the contents of the request and return invocation messages later in this article.

Implementing the NeverOnASunday Handler
Before starting work, consider whether you want to update your existing version of Enterprise Library, or create an entirely new modified version of the library. Bear in mind that because the new handler does not fundamentally change any other parts of the library, the easiest approach is to integrate it into your existing version. To do that, you edit the project in your EntLibSrc folder where you installed the source code. If you did not install the source code when you originally installed Enterprise Library, you can rerun setup to install it.

The sample code for this article contains the new and updated files for you to use with Enterprise Library 3.0 to implement the custom NeverOnASunday handler. Note that the Resources files in the two Properties folders, and the PolicyInjectionNodeMapRegistrar.cs and PolicyInjectionCallHandlerCommandRegistrar.cs files all replace existing files, while the rest are additions to the existing Enterprise Library files. If you have previously edited these files, you must merge the resource entries and the changes detailed in the later section “Registering the Configuration Node Class” with your existing files.

You’ll find a complete version of this simple NeverOnASunday handler implementation in a single class located in the file SimpleNeverOnASundayCallHandler.cs. As you will see later, the actual handler used in the example code contains some extra features. However, to help you understand the basic approach, the next code example shows the simple implementation (minus the XML comments).

Notice that the class implements the ICallHandler interface, and is decorated with a ConfigurationElementType attribute specifying the class that exposes the configuration data for the handler (NeverOnASundayCallHandlerData). You will see this class later in this article:

   [ConfigurationElementType(typeof(NeverOnASundayCallHandlerData))]   public class NeverOnASundayCallHandler : ICallHandler   {         public static readonly Boolean DefaultNotSaturdayEither = false;         private Boolean notSaturday;         public NeverOnASundayCallHandler() : this(DefaultNotSaturdayEither)      { }         public NeverOnASundayCallHandler(Boolean notSaturdayEither)      {         this.notSaturday = notSaturdayEither;      }         public Boolean NotSaturdayEither      {         get { return notSaturday; }         set { notSaturday = value; }      }             public IMethodReturn Invoke(IMethodInvocation input,          GetNextHandlerDelegate getNext)      {         Exception ex = null;         // Calculate the current weekday.         GregorianCalendar cal = new GregorianCalendar();         DayOfWeek weekDay = cal.GetDayOfWeek(DateTime.Now);         if (weekDay == DayOfWeek.Sunday)           {            // Create the Exception to return and the return message.            ex = new Exception(Resources.NotSundayExceptionMessage);         }         if ((notSaturday == true) && (weekDay == DayOfWeek.Saturday))           {            // Create the Exception to return and the return message.            ex = new Exception(Resources.NotSaturdayExceptionMessage);         }         if (ex != null)         {            // Return the exception in the return message.            IMethodReturn msg = input.CreateExceptionMethodReturn(ex);            return msg;           }         else         {            // Do nothing except invoke the next handler.             return getNext()(input, getNext);           }      }   }

The handler exposes a public property named NotSaturdayEither that specifies whether it should block invocations on Saturday as well as Sunday. The default for this parameter is False, as exposed by the field named DefaultNotSaturdayEither. Two constructors allow code to instantiate the handler with either the default value for the NotSaturdayEither property, or with a specified value.

The Invoke method simply checks the current day of the week, and either invokes the next handler; or creates an exception and returns it in a ReturnMessage instance to the caller.

Finally, notice that the handler uses values from the Resources for the project for the exception-message strings. This makes it easier to localize the code, or change the messages in the future if required. However, because this example handler uses the GregorianCalendar and English day names, it will not localize in any kind of sensible way to cultures that do not use this calendar! Table 1 shows the entries added to the Resources.resx file in the Properties folder of the PropertyInjection.CallHandlers project.

Table 1. The table lists the values added to the Resources.resx file in the PolicyInjection.CallHandlers project.
NameValue
NotSaturdayExceptionMessageInvocation not permitted on a Saturday
NotSundayExceptionMessageInvocation not permitted on a Sunday

Accessing Data in Request and Return Messages
To demonstrate how you can access the contents of the request and return invocation messages, the handler used in the example application contains extra code that displays the target object and member name, any parameters passed to the target member, and any value that the target member returns. This version of the handler is a single class located in the file NeverOnASundayCallHandler.cs (in the App BlocksSrcPolicyInjectionCallHandlers subfolder of the Enterprise Library source code). If you are editing the existing Enterprise Library project, you must ensure that you place the class in the correct namespace (Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers), and add using statements for the other namespaces required. Open the sample project to see the complete file.

The next two code fragments show the new code added to the Invoke method in this version of the handler (the remainder of the class is the same as you saw in the previous section of this article). The first new code section obtains a reference to the current ASP.NET context so that it can write to the Trace object to display data about the target member invocation. It then accesses and displays the Target and MethodBase properties of the IMethodInvocation instance passed to the Invoke method to show the name of the target object and the name of the target member (method or property accessor).

Next, the code accesses the Inputs property of the IMethodInvocation instance, which contains a reference to an object that implements the IParameterCollection interface, and iterates through this list accessing the individual parameters passed to the target member. For each one, the code displays the parameter name and the value (or the type name if it is not a value type):

   public IMethodReturn Invoke(IMethodInvocation input,       GetNextHandlerDelegate getNext)   {      // Display information about the method/property call.      HttpContext context = HttpContext.Current;      context.Trace.Warn("NOASHandler", "Target Object: " +          input.Target.ToString());      context.Trace.Warn("NOASHandler", "Target Method: " +           input.MethodBase.Name);      IParameterCollection targetInputs = input.Inputs;      for (Int32 i = 0; i < targetInputs.Count; ++i)      {         context.Trace.Warn("NOASHandler", "Parameter '" +             targetInputs.ParameterName(i) + "' = " +             targetInputs[i].ToString());     }     ...

The next section of the code (not shown here) is the same as in the simple version of the handler. It calculates the current weekday, and generates the appropriate exception and return message if it is a day when invocation is not permitted. However, if invocation is permitted, the else clause contains some extra code in this version of the handler to extract and display the return value.

To do this, the first step is to generate a return message as an implementation of the IMethodReturn interface, using the CreateMethodReturn method of the original invocation message (the IMethodInvocation implementation). The simple version of the handler used this code to invoke the next handler and then pass the return message back to the caller:

   return getNext()(input, getNext);  

However, to generate a return message directly, the code must execute the getNext delegate to invoke the next handler, and then use the return value from the getNext delegate in the CreateMethodReturn method. This method also requires the array of parameters passed to the method, available from the Arguments property of the original invocation message:

      ...      else         {         // Invoke the next handler and create the return message using the         // return value from the target (the next handler or the target          // object) and the parameters passed to the target.         IMethodReturn returnMessage = input.CreateMethodReturn(            getNext()(input, getNext).ReturnValue, input.Arguments);            // Display the return value of the method/property (if any)         Object returnValue = returnMessage.ReturnValue;         if (returnValue != null)         {            context.Trace.Warn("NOASHandler", "Return Value: " +                 returnValue.ToString());         }            // Return the invocation return message to the caller.         return returnMessage;      }   }

Having generated the return message, the code can display the return value from the target member (if any), and then return the message to the caller.

Creating the Handler's Configuration Class
To read configuration data, the new handler requires a matching configuration class, which you'll find in the new file NeverOnASundayCallHandlerData.cs (in the App BlocksSrcPolicyInjectionCallHandlersConfiguration subfolder of the Enterprise Library source code). If you are editing the existing Enterprise Library project, you must ensure that you place the class in the correct namespace (Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers.Configuration), and add using statements for the other namespaces required. Open the sample project to see the complete file.

The configuration class inherits from the CallHandlerData base class, and defines the name of the configuration file attribute used in the element that will add this handler to the configuration?in this case the attribute name is notSaturdayEither. The configuration class also contains two constructors:

  • A default constructor that sets the NotSaturdayEither property to the default value exposed by the field named DefaultNotSaturdayEither in the NeverOnASundayCallHandler class.
  • A constructor that takes a handler name and generates an instance of the hander with that name by calling the base class constructor. This also sets the default value for the NotSaturdayEither property.

Finally, the configuration class exposes the NotSaturdayEither property, using a ConfigurationProperty attribute to specify the attribute name that corresponds to this property:

   [Assembler(typeof(NeverOnASundayCallHandlerAssembler))]   public class NeverOnASundayCallHandlerData : CallHandlerData   {         private const string NotSaturdayPropertyName = "notSaturdayEither";               public NeverOnASundayCallHandlerData()      {         NotSaturdayEither = NeverOnASundayCallHandler.            DefaultNotSaturdayEither;      }         public NeverOnASundayCallHandlerData(string handlerName)         : base(handlerName, typeof(NeverOnASundayCallHandler))      {         NotSaturdayEither = NeverOnASundayCallHandler.            DefaultNotSaturdayEither;      }         [ConfigurationProperty(NotSaturdayPropertyName)]      public Boolean NotSaturdayEither      {         get { return (Boolean)base[NotSaturdayPropertyName]; }         set { base[NotSaturdayPropertyName] = value; }      }   }

Notice that the class is decorated with an [Assembler] attribute that specifies the class that Enterprise Library will use, via ObjectBuilder, to create configured instances of the handler. The next code fragment shows the NeverOnASundayCallHandlerAssembler class:

   public class NeverOnASundayCallHandlerAssembler      : IAssembler   {         public ICallHandler Assemble(IBuilderContext context,         CallHandlerData objectConfiguration,         IConfigurationSource configurationSource,         ConfigurationReflectionCache reflectionCache)      {          NeverOnASundayCallHandlerData handlerConfiguration              = (NeverOnASundayCallHandlerData)objectConfiguration;          return new NeverOnASundayCallHandler(             handlerConfiguration.NotSaturdayEither);      }   }

This is the generic technique for creating configuration data instances in Enterprise Library?implementing the Assemble method that takes a range of parameters. After doing that, all that remains is to cast the objectConfiguration to the required configuration type (NeverOnASundayCallHandlerData in this case) and generate a new instance of the handler class using the value of the NotSaturdayEither property from the NeverOnASundayCallHandlerData instance.

At this point the new handler will work, but only if you manually edit the application configuration. The next step is to add the design support classes and code for the handler so developers and administrators can configure it using Enterprise Library's configuration tools.

Integrating the Handler with the Configuration Tools
To integrate a provider with the Enterprise Library configuration tools, the tools must contain classes that define the type of editing node(s) to create for that provider. Enterprise Library providers include common items such as database providers, cache backing stores, and Windows Event Log providers. However, the Matching Rules and Call Handlers used by the Policy Injection Application Block are also providers?they act as extension points for the block that allow developers to extend the behavior of the block to suit their own requirements.

Creating the Configuration Editor Node Class
The custom handler described in this article is quite simple, and requires only a single configuration editor node, and no child nodes unlike more complex providers such as those used in the Logging and Caching Application Blocks. The configuration editor node is defined in the NeverOnASundayCallHandlerNode class (see the file NeverOnASundayCallHandlerNode.cs in the App BlocksSrcPolicyInjectionCallHandlersConfigurationDesign subfolder of the Enterprise Library source code). If you are editing the existing Enterprise Library project, you must ensure that you place the class in the correct namespace (Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers.Configuration.Design), and add using statements for the other namespaces required. Open the sample project to see the complete file.

The following code shows the NeverOnASundayCallHandlerNode class, which inherits from the CallHandlerNode base class that implements most of the required functionality. The class contains two constructors. The first generates instances with the default handler name, as stored in the Resources file. The second constructor accepts an existing instance of the NeverOnASundayCallHandlerData configuration data class containing the handler name, and sets the value of the NotSaturdayEither property using the data in the existing instance:

   public class NeverOnASundayCallHandlerNode : CallHandlerNode   {         Boolean notSaturday;         public NeverOnASundayCallHandlerNode()          : this(new NeverOnASundayCallHandlerData(         Resources.NeverOnASundayCallHandlerNodeName))      { }         public NeverOnASundayCallHandlerNode(         NeverOnASundayCallHandlerData callHandlerData)         : base(callHandlerData)      {         this.notSaturday = callHandlerData.NotSaturdayEither;      }         [SRDescription("NotSaturdayDescription", typeof(Resources))]      [SRCategory("CategoryGeneral", typeof(Resources))]      public Boolean NotSaturdayEither      {         get { return notSaturday; }         set { notSaturday = value; }      }         public override CallHandlerData CreateCallHandlerData()      {         NeverOnASundayCallHandlerData callHandlerData =             new NeverOnASundayCallHandlerData(Name);         callHandlerData.NotSaturdayEither = notSaturday;         return callHandlerData;      }   }

The remaining code in the NeverOnASundayCallHandlerNode class is the accessor for the NotSaturdayEither property, which specifies the category name and description (taken from the Resources file) to display in the configuration tools for this property, and the required override of the CreateCallHandlerData method. The CallHandlerNode base class defines this as an abstract method, which handlers implement to return an instance of the appropriate concrete handler configuration data class.

In the example handler, the CreateCallHandlerData method creates a new NeverOnASundayCallHandlerData instance using the name exposed by the base class, sets its NotSaturdayEither property using the value in this configuration node, and returns it to the caller.

As you saw earlier in the handler class, using values from the Resources for the project makes it easier to localize the code, or change the text in the future as required. The Resources.resx file contains the name of the node and the description of the single property for use in the configuration tools. Table 2 shows all the entries added to the Resources.resx file in the Properties folder of the PropertyInjection.CallHandlers.Configuration.Design project. The last two values are used in the code you will see later that registers the node and commands for the handler with the configuration tools.

Table 2: Values added to the Resources.resx file in the PolicyInjection.CallHandlers.Configuration.Design project.
NameValue
NeverOnASundayCallHandlerNodeNameNever On A Sunday Handler
NotSaturdayDescriptionTrue if handler will prevent invocation on a Saturday
NeverOnASundayCallHandlerCommandTextNever On A Sunday Handler
AddNeverOnASundayCallHandlerCommandTextLongAdd Never On A Sunday Handler

Registering the Configuration Node Class
The final steps for integrating the new handler with the configuration tools are to register the node and the commands that manipulate the node. These steps require you to edit the existing registration files within Enterprise Library.

As the configuration tools load and initialize, they call the Register method in two files within each application block. The first file, named PolicyInjectionNodeMapRegistrar.cs (in the App BlocksSrcPolicyInjectionCallHandlersConfigurationDesign subfolder of the Enterprise Library source code), registers the configuration node class. The following code shows what you must add to the Register method override in this class:

   public override void Register()   {      ... code to register other handler nodes is here ...         // register the new custom handler node      base.AddMultipleNodeMap(Resources.NeverOnASundayCallHandlerNodeName,         typeof(NeverOnASundayCallHandlerNode),         typeof(NeverOnASundayCallHandlerData));   }

You can see that this code uses the name of the node ("Never On A Sunday Handler") as stored in the Resources file. The AddMultipleNodeMap method takes as parameters this name, the type that implements the handler node, and the type that stores the configuration data for the handler.

The final step is to modify the Register method override in the file that registers commands. This file, PolicyInjectionCallHandlerCommandRegistrar.cs (in the App BlocksSrcPolicyInjectionCallHandlersConfigurationDesign subfolder of the Enterprise Library source code), contains calls to methods of the CommandRegistrar base class. These methods add the default commands (such as New and Rename), commands to reorder the handlers, and commands to add the specific child nodes for each of the handlers.

For the custom NeverOnASunday handler, you must add calls to the AddDefaultCommands, AddMoveUpDownCommands, and AddMultipleChildNodeCommand methods. Each of these methods accepts the handler node class type. The AddMultipleChildNodeCommand method also requires the short and long text to display in the console tooltips and status bar (taken from the Resources file), and the type of the parent node for this node (the "Handlers" node, as shown near the beginning of this article in Figure 1):

   public override void Register()   {      ... code to register commands for other handlers is here ...         // register the commands required to be available for this handler      base.AddDefaultCommands(typeof(NeverOnASundayCallHandlerNode));      base.AddMoveUpDownCommands(typeof(NeverOnASundayCallHandlerNode));      base.AddMultipleChildNodeCommand(         Resources.NeverOnASundayCallHandlerCommandText,         Resources.AddNeverOnASundayCallHandlerCommandTextLong,         typeof(NeverOnASundayCallHandlerNode),         typeof(CallHandlersCollectionNode));   }

This completes integration of the new custom NeverOnASunday handler with the configuration tools. As Figure 1 showed, you can now add the handler to an application's configuration, and set the NotSaturdayEither property. If you open the resulting configuration file in a text editor, you will see the element that adds the handler to the configuration:

   ...                                                                    ...

Implementing the NeverOnASundayCallHandlerAttribute
At this point, you can configure your applications to use the new handler by editing the configuration files manually or with the Enterprise Library configuration tools, but you can't add the new handler with a code attribute. Before looking at how you can use the new handler in an ASP.NET application, it's worth considering creating a handler attribute that developers can use to add the handler to a class, method, or property at design-time. This is useful when a developer wants to set the behavior of an object and ensure that specific handlers always execute, irrespective of the current configuration of the Policy Injection Application Block.

A handler attribute simply has to generate an instance of the relevant handler class, and set any properties for which the attribute specifies values. Usually, the attribute will expose the same properties as the handler, and pass the values through to the handler.

The HandlerAttribute base class provides much of the implementation required for a handler attribute. All your attribute class must do is provide the required constructors, and implement the CreateHandler method that returns a configured instance of the appropriate handler.

For the NeverOnASunday handler, the file NeverOnASundayCallHandlerAttribute.cs (in the App BlocksSrcPolicyInjectionCallHandlers subfolder of the Enterprise Library source code) implements a suitable handler attribute. Once again, if you're editing the existing Enterprise Library project, you must ensure that you place the class in the correct namespace (Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers), and add using statements for the other namespaces required. The sample project contains the complete file.

The following listing shows the attribute class. Notice the AttributeUsage attribute that specifies where the developer can use this handler?in this case on a class, a method, or a property. The class also contains two constructors. The first constructor sets the value of the private notSaturday variable to the default value of the NotSaturdayEither property using the public DefaultNotSaturdayEither field exposed by the NeverOnASundayCallHandler class. The second constructor sets the value to that passed to the constructor from the attribute on the target member:

   [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property |       AttributeTargets.Method)]   public class NeverOnASundayCallHandlerAttribute : HandlerAttribute   {         private Boolean notSaturday;         public NeverOnASundayCallHandlerAttribute()      {         notSaturday = NeverOnASundayCallHandler.DefaultNotSaturdayEither;      }         public NeverOnASundayCallHandlerAttribute(Boolean notSaturdayEither)      {         notSaturday = notSaturdayEither;      }         public override ICallHandler CreateHandler()      {         return new NeverOnASundayCallHandler(notSaturday);      }   }

The remaining code is the CreateHandler method that simply generates a new instance of the NeverOnASunday handler, specifying the value for the NotSaturdayEither property. This is all that is required to create a handler attribute.

Rebuilding Enterprise Library
After all the new and updated code is in place, you must recompile the Policy Injection Application Block and the configuration tools. In fact, the easiest way is to use the BuildLibraryAndCopyAssemblies.bat utility installed by default in your EntLibSrcApp Blocks folder. This recompiles the entire Enterprise Library, and copies all the assemblies and the configuration tools to the EntLibSrcApp BlocksBin folder, so it's ready for use.

Using the NeverOnASunday Handler in ASP.NET
Finally, this article demonstrates how you might use the new custom NeverOnASunday handler and the matching handler attribute in a simple ASP.NET application. Create a new Web Site and a suitable target object, or open an existing Web site or application that contains a suitable target object. The object must either inherit MarshalByRefObject, or have an interface definition available. The sample application contains an example of an object named DemoClass, which inherits from MarshalByRefObject, and exposes three methods and a property:

  • The GetCurrentWeekday method, which returns the name of the current weekday:
  •    public String GetCurrentWeekday()   {      GregorianCalendar cal = new GregorianCalendar();      return cal.GetDayOfWeek(DateTime.Now).ToString();   }
  • The AlwaysWorksOnSunday method, which returns a message that?depending on the value of the returnWeekday parameter?can include the current weekday name. Notice that this method carries the [ApplyNoPolicies] attribute, which always prevents the Policy Injection Application Block from generating a handler pipeline for this member:
  •    [ApplyNoPolicies]   public String AlwaysWorksOnSunday(Boolean returnWeekday)   {      String returnValue = "Yes, I am working today";      if (returnWeekday == true)      {         GregorianCalendar cal = new GregorianCalendar();         String dayName = cal.GetDayOfWeek(DateTime.Now).ToString();         returnValue += ", even though it's " + dayName;      }      return returnValue;   }
  • The NeverWorksOnSunday method, which is the same as the AlwaysWorksOnSunday method but carries an instance of the custom handler attribute [NeverOnASundayCallHandlerAttribute] that sets the value of the NotSaturdayEither property to true:
  •    [NeverOnASundayCallHandlerAttribute(true)]   public String NeverWorksOnSunday(Boolean returnWeekday)   {      String returnValue = "Yes, I am working today";      if (returnWeekday == true)      {         GregorianCalendar cal = new GregorianCalendar();         String dayName = cal.GetDayOfWeek(DateTime.Now).ToString();         returnValue += ", even though it's " + dayName;      }      return returnValue;   }
  • The GetTodaysDate property, which returns the current date and time:
  •    public DateTime GetTodaysDate   {      get { return DateTime.Now; }   }
?
Figure 2. Sample Application Configuration: The figure shows the configuration of the sample application in the Visual Studio 2005 Configuration Editor.

Now use the configuration tools to add the Policy Injection Application Block to the application, and configure Policies that match members of the target object?with each Policy using one or more Matching Rules. Then add the NeverOnASunday handler to the Handlers section of each Policy. The example code for this article has the configuration already set up, though you can edit it as required. Figure 2 shows the default configuration open in the Visual Studio 2005 Configuration Editor.

From Figure 2, you can see that the example application uses two policies, each with a Type Matching Rule that selects the DemoClass object and a Matching Rule that selects members of the DemoClass object:

  • The NotSaturdayEitherPolicy selects the GetCurrentWeekday and the AlwaysWorksOnSunday methods using a member Name Matching Rule, and applies the NeverOnASunday handler with the NotSaturdayEither property set to true.
  • The SaturdayOKPolicy selects the GetTodaysDate property, and applies the NeverOnASunday handler with the NotSaturdayEither property set to false.

Note that neither of these policies applies the NeverOnASunday handler to the NeverWorksOnSunday method.

Editing the Application Configuration
The built-in VS2005 Configuration Editor and the default Enterprise Library Configuration Console will not work directly with applications that use unsigned versions of the Enterprise Library assemblies located in the Bin folder. Instead, you can use the version of the Configuration Console in your EntLibSrc folder, or change the behavior of the VS 2005 Configuration Editor to use the unsigned assemblies in your EntLibSrc folder rather than the signed assemblies in the Program Files folder. To change the behavior of the VS 2005 Configuration Editor:

  • Use the File menu to add a new Web Site to the project so that VS creates a root-level solution node containing the two projects.
  • Select the solution node and open the Properties window by pressing F4.
  • Change the EnterpriseLibraryConfigurationSet property to EntLib3Src, and click OK in the warning dialog.
  • Select "Save As" from the File menu and save the solution file in the project folder.
  • Select the project node for the new Web Site project you added, right-click, and select Remove.
  • Select Save All from the File menu.
  • Use the saved solution file (.sln) to open the project in future.

The example files for this article contain suitable solution files that you can use to open the projects with the appropriate configuration setting for the VS2005 Configuration Editor.

The ASP.NET Page in the Example Application
The ASP.NET page Default.asp and the associated code-behind file (provided in both C# and VB.NET in the downloadable code) provide buttons to call the methods of the DemoClass object and display the return value in a Label control. For example, here's the code that runs when you click the first button in the page to get the current weekday name:

   protected void btn_WeekdayName_Click(object sender, EventArgs e)   {      try      {         DemoClass target = PolicyInjection.Create();         lblResult.Text = target.GetCurrentWeekday();      }      catch (Exception ex)      {         lblResult.Text = "ERROR: " + ex.Message;      }   }

Notice that the code handles any error that may arise. The NeverOnASunday handler will raise an exception if it detects invocation on a Sunday (or a Saturday if the NotSaturdayEither property is true). Figure 3 shows the simple example page.

?
Figure 3. Example Page: Here's the ASP.NET example page, showing the result of calling the GetCurrentWeekday method of the DemoClass object.
?
Figure 4. Tracing Handler Calls: Here's the pertinent trace information generated by calling the NeverWorksOnSunday method on a weekday.

Viewing the Results of the NeverOnASunday Handler
Of course, seeing the results of the target object members executing is fine, but to test the handler and understand what is really happening you need more information about the invocation. The example ASP.NET page has tracing enabled in the @Page directive. Therefore, each time you execute one of the methods of the DemoClass object, you can view the trace information to see the values inserted by the code in the NeverOnASunday handler.

For example, Figure 4 shows the section of the trace information that contains the output generated by the handler when you call the NeverWorksOnSunday method on a weekday (in this case, Tuesday). You can see the target object name, the method name, the single parameter name and its value, and the return value.

If you run the application on a Saturday and a Sunday (or just change your system's date appropriately) you can experiment to see the effects of the NeverOnASunday handler when it prevents invocation of members of the target class.

Figure 5 shows the example application running on a Sunday, where a call to the NeverWorksOnSunday method fails and the page displays the message contained in the Exception that the Policy Injection Application Block returns.

?
Figure 5. No Sunday Execution: The exception returned when calling the NeverWorksOnSunday method on a Sunday.
?
Figure 6. Tracing Information from Sunday: The trace information generated by calling the NeverWorksOnSunday method on a Sunday.

If you examine the trace information in this case (see Figure 6), you can see that the handler executed the pre-processing code that displays details of the invocation message. It does not, of course, display any return value because the handler short-circuits and the post-processing code does not execute.

Notice as you experiment with the example that the NeverOnASunday handler is activated for every method/property of the target object except for the AlwaysWorksOnSunday method. When you call this method, irrespective of the current day and the fact that the NotSaturdayEitherPolicy configuration Policy selects this method, there are no entries in the trace information. This is because the code for this method within the DemoClass object carries the [ApplyNoPolicies] attribute.

Conversely, neither of the Policies in the example application configuration selects the AlwaysWorksOnSunday method, yet it always has a pipeline containing the NeverOnASunday handler?on both Saturday and Sunday. This is because the code for this method within the DemoClass object carries the custom handler attribute [NeverOnASundayCallHandlerAttribute(true)].

Now, Build Your Own
In this article, you have seen how the Policy Injection Application Block uses call handlers within the pipeline it creates between client code and a target object. You have also seen how easy it is to create custom handlers that implement the behavior you require for your own applications. The articles discusses the requirements for a handler, the basic implementation, and the techniques for more complex interaction with the invocation input and output messages.

The Policy Injection Application Block is a powerful tool that allows you to specify Policies through configuration, or through the use of attributes applied directly to classes and class members. This article shows how you can create custom handler attributes that developers can use to apply your custom handler within their code.

To make it easy to use custom providers, such as handlers for the Policy Injection Application Block, you can take advantage of the extensible configuration system in Enterprise Library. By creating suitable configuration node classes, and modifying the registration process, your custom handler becomes available as a standard configuration option within the Enterprise Library Configuration Console and the Visual Studio 2005 Configuration Editor.

Finally, this article demonstrates how you can use a custom handler and its related handler attribute in a simple ASP.NET application, and view its effects. I urge you to download the sample code, and experiment with it. For more information about Enterprise Library 3.0, see. http://www.codeplex.com/entlib/.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist