A Custom Policy Injection Application Block Handler

"Creating the NeverOnASunday Call Handler"

 

©2007 Alex Homer, http://www.daveandal.net

 

 

I 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 you can make policy injection work for you. As part of Enterprise Library, the Policy Injection Application Block allows you to control the behavior of your applications through configuration, including enforcing this configuration using Group Policy. The example handler, by default, blocks access on a Sunday to any target method or property specified through configuration, or with a custom attribute, and 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 – Adding the NeverOnASunday handler to an application's configuration

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:

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. If not found, 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 listing. 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 you can 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 a whole new modified version of the library. As the new handler does not fundamentally change any other parts of the library, the easiest approach is to integrate it into your existing version by editing the project in your EntLibSrc folder where you installed the source code. If you did not install this when you originally installed Enterprise Library, you can rerun setup to install the source code.

A simple implementation of the NeverOnASunday handler is a single class located in the new 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 listing shows the simple implementation (minus the XML comments). Open the sample project to see the complete file.

Notice that the class implements the ICallHandler interface, and is decorated with a ConfigurationElementType attribute that specifies 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 if it will block invocations on a Saturday as well as a Sunday. The default for this parameter is False, as exposed by the field named DefaultNotSaturdayEither. Two constructors allow code to instantiate the handler with the default value for the NotSaturdayEither property, and 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 other 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 – Values added to the Resources.resx file in the PolicyInjection.CallHandlers project

Name

Value

NotSaturdayExceptionMessage

Invocation not permitted on a Saturday

NotSundayExceptionMessage

Invocation 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 Blocks\Src\PolicyInjection\CallHandlers 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 listings 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 Configuration Class for the Handler

To be able to read configuration data, the new handler requires a matching configuration class. This is the new file NeverOnASundayCallHandlerData.cs (in the App Blocks\Src\PolicyInjection\CallHandlers\Configuration 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 <add> element that will add this handler to the configuration – in this case the attribute name is notSaturdayEither. The configuration class also contains two constructors:

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 listing shows the NeverOnASundayCallHandlerAssembler class.

public class NeverOnASundayCallHandlerAssembler

  : IAssembler<ICallHandler, CallHandlerData>

{

 

  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, by implementing the Assemble method that takes a range of parameters. All that then remains is to cast the objectConfiguration to the required configuration type (NeverOnASundayCallHandlerData) 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 requires manual editing of the application configuration. Therefore, the next step is to add the design support classes and code for the handler so that users can configure it within the configuration tools provided with Enterprise Library.

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 the more obvious 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 only requires a single configuration editor node. Unlike the more complex providers (as used in the Logging and Caching Application Blocks), no child nodes are required for the example provider. The configuration editor node is the class named NeverOnASundayCallHandlerNode in the file NeverOnASundayCallHandlerNode.cs (in the App Blocks\Src\PolicyInjection\CallHandlers\Configuration\Design 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 next listing 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 one 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

Name

Value

NeverOnASundayCallHandlerNodeName

Never On A Sunday Handler

NotSaturdayDescription

True if handler will prevent invocation on a Saturday

NeverOnASundayCallHandlerCommandText

Never On A Sunday Handler

AddNeverOnASundayCallHandlerCommandTextLong

Add 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 of these files, named PolicyInjectionNodeMapRegistrar.cs (in the App Blocks\Src\PolicyInjection\CallHandlers\Configuration\Design subfolder of the Enterprise Library source code), registers the configuration node class. The following listing shows the code 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 Blocks\Src\PolicyInjection\CallHandlers\Configuration\Design 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), add the commands to reorder the handlers, and add the specific child nodes for each of the handlers.

For the custom NeverOnASunday handler, you must add calls to the AddDefaultCommands method, AddMoveUpDownCommands method, and AddMultipleChildNodeCommand method. 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 <add> element that adds the handler to the configuration:

...

<policyInjection>

  <policies>

    <add name="Policy">

      <handlers>

 

        <add notSaturdayEither="true"

             type="Microsoft...PolicyInjection.CallHandlers.NeverOnASundayCallHandler,

                   Microsoft...PolicyInjection.CallHandlers,

                   Version=3.0.0.0, Culture=neutral, PublicKeyToken=null"

             name="Never On A Sunday Handler" />

 

      </handlers>

    </add>

  </policies>

</policyInjection>

...

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 is 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 the developer wants to fix 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 Blocks\Src\PolicyInjection\CallHandlers subfolder of the Enterprise Library source code) implements a suitable handler attribute. As usual, 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 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.

Building Enterprise Library

Once 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 EntLibSrc\App Blocks folder. This recompiles the entire Enterprise Library, and copies all the assemblies and the configuration tools to the EntLibSrc\App Blocks\Bin folder ready for use.     

Using the NeverOnASunday Handler in ASP.NET

Finally, this article demonstrates usage of 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 (see http://www.daveandal.net/articles/piab-handler/) contains an example of an object, named DemoClass, which inherits from MarshalByRefObject, and exposes three methods and a property:

public String GetCurrentWeekday()

{

  GregorianCalendar cal = new GregorianCalendar();

  return cal.GetDayOfWeek(DateTime.Now).ToString();

}

[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;

}

[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;

}

public DateTime GetTodaysDate

{

  get { return DateTime.Now; }

}

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.

Figure 2 – The configuration of the sample application

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:

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 instead of the signed assemblies in the Program Files folder. To change the behavior of the VS 2005 Configuration Editor:

  1. 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.
  2. Select the solution node and open the Properties window by pressing F4.
  3. Change the EnterpriseLibraryConfigurationSet property to EntLib3Src, and click OK in the warning dialog.
  4. Select Save <solution-name> As from the File menu and save the solution file into the project folder.
  5. Select the project node for the new Web Site project you added, right-click, and select Remove.
  6. Select Save All from the File menu.
  7. 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) provide buttons to call the methods of the DemoClass object and display the return value in a Label control. For example, this is 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<DemoClass>();

    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, and a Saturday if the NotSaturdayEither property is true. Figure 3 shows the simple example page.

Figure 3 – The ASP.NET example page, showing the result of calling the GetCurrentWeekday method of the DemoClass object.

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.

Figure 4 – The trace information generated by calling the NeverWorksOnSunday method on a weekday.

If you run the application on a Saturday and a Sunday, you can experiment to see the effects of the NeverOnASunday handler when it prevents invocation of members of the target class. Of course, an easy way is to use the Windows Time and Date settings to set the current date to a Saturday or Sunday...!

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 – The exception returned when 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.

Figure 6 – The trace information generated by calling the NeverWorksOnSunday method on a Sunday.

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)].

Summary

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 related handler attribute in a simple ASP.NET application, and view its effects. For more information about Enterprise Library 3.0, see http://www.codeplex.com/entlib/. To obtain the sample code for this article, go to http://www.daveandal.net/articles/piab-handler/.

 

©2007 Alex Homer, http://www.daveandal.net