This project is read-only.

Action Catalog

Problem

In smart client applications, you must conditionally execute code (an "action") based on aspects of a program that can change at run time. The logic for the evaluation is not related to the logic of the action that executes if the result of the evaluation is true. You require the ability to add, replace, or remove conditions for the evaluation at run time. You also require the ability to replace the logic that evaluates conditions at run time. Finally, you want certain conditions to be evaluated for all actions and other conditions to apply to only specific actions.
An example is an application that shows a view in a workspace only if the current user is associated with a specific role. The action is the code that shows the view. The condition is the code that determines the user's role and evaluates if the action will execute based on the current user's role. The logic for the condition (determining the role and evaluating if it applies to the action) is not related to the logic for the action (showing the view). A change to the logic that evaluates the user's role has no side-effects on the logic that shows the view.

Solution

The solution is to introduce an object (an "action catalog") that evaluates one or more conditions ("condition objects") and executes business logic (an "action") on behalf of other objects. A condition object is responsible for performing the evaluation for a specified action. For example, a condition object can evaluate whether the action to show a view can execute based on the current user's role.
Condition objects can be specific to an action or they can apply to all actions. The action catalog executes an action only if all conditions objects associated with that action return true for their evaluation.
The action catalog object is an instance of the ActionCatalog class. This class contains the following collections:
  • A list of condition objects that apply to all actions
  • A dictionary that maps action names to a list of condition objects specific to that action
  • A dictionary that maps action names to delegates (the action code that is executed if the evaluation returns true)
The class exposes methods to register condition objects for specific actions or all actions. Figure 1 illustrates the fields and methods available in the ActionCatalog class contained in the Bank Branch Client reference implementation.

Figure 1 - Fields and methods of the ActionCatalog class.png
Figure 1
Fields and methods of the ActionCatalog class

You can use the CanExecute method to obtain the result of an evaluation for an action. One of the parameters of the CanExecute method is a WorkItem object. This means the condition objects can use the current state of the application in the evaluation. The Execute method calls the CanExecute method, and if the result is true, executes the action.
Developers register an action catalog as a service. This means an application can have more than one action catalog, but each WorkItem can have at most one action catalog. Figure 2 illustrates an application that contains two action catalog instances.

Figure 2 - ActionCatalog services.png
Figure 2
ActionCatalog services

Liabilities

An action catalog is a service associated with a WorkItem. This means that you must be aware of the WorkItem hierarchy when you use the action catalog.
All actions (the methods identified by an action name) have the same signature, with two arguments. The arguments are the caller object and the target object. The target is an arbitrary object.

How to use the Action Catalog

You can use the action catalog to control whether a business action is executed.
Note: If you used the Smart Client Development guidance package to create your solution, the Action Catalog is already part of the Infrastructure.Interface project. This topic explains how to use the Action Catalog if you did not use the Smart Client Develpoment guidance package to create your solution. If you created your solution using the guidance package, see the topic How to: Use the Action Catalog in the Smart Client Software Factory help.

Prerequisites

To prepare a solution for this topic:
  1. Add a module to your solution and create a new view within the module project.
  2. Add a reference to the ActionCatalog.Interface project in your module.
  3. Add a reference to the ActionCatalog and ActionCatalog.Interface projects in the Shell project.

Steps

You will use the action catalog to control the execution of a business action by implementing code that performs the following:
  1. Create and register the action catalog service.
  2. Add the action strategy to ObjectBuilder.
  3. Define an action condition.
  4. Define the action.
  5. Relate the action with section of code.
  6. Add the action to the action catalog.
  7. Conditionally execute the action.
Note: The following procedure uses “MyView” to refer to the view you created in your business module.

To create and register the action catalog service:
1. Register the ActionCatalogService in the root WorkItem. To do this, you can register it in your main shell application class as shown in the following code.

protected override void AddServices()
{
         base.AddServices();
         RootWorkItem.Services.AddNew<ActionCatalogService, IActionCatalogService>();
}


To add the action strategy to ObjectBuilder:
1. Add the following using statement to the main shell application class.

using Microsoft.Practices.ObjectBuilder;


2. Override the AddBuilderStrategies method and add the ActionStrategy to ObjectBuilder. Specify the Initialization builder stage for the strategy. This strategy reflects on the methods of objects created by ObjectBuilder and adds entries into the action catalog for methods with an Action attribute.

protected override void AddBuilderStrategies(Builder builder)
{
         base.AddBuilderStrategies(builder);
         builder.Strategies.AddNew<ActionStrategy>(BuilderStage.Initialization);
}


To define an action condition:
1. Create a public class in your module project. Change the class definition to implement the IActionCondition interface. All action condition services implement the IActionCondition interface. In the following code, a class named MyActionCondition is declared.

public class MyActionCondition : IActionCondition


2. Add the following using statements to the class.

using System.Threading;
using Microsoft.Practices.CompositeUI;


3. Implement the CanExecute method. The action catalog calls this method before executing a business action to determine if the action can be executed. The following code shows a sample implementation.

public bool CanExecute(string action, WorkItem context, object caller, object target)
{
          if (action == "ShowMyView" && Thread.CurrentPrincipal.Identity.Name == @"DOMAIN\username")
          {
                    return true;
          }
          else
          {
                    return false;
          }
}


4. Create and register the action condition class with the action catalog when your module loads. Register the action condition as a general condition. This means that this action condition is used for all actions, regardless of the action name. To do this, obtain a reference to the IActionCatalogService and invoke the RegisterGeneralCondition method as shown in the following code.

IActionCatalogService catalog = rootWorkItem.Services.Get<IActionCatalogService>();
catalog.RegisterGeneralCondition(new MyActionCondition());


To define an action:
1. Create a class named ActionNames in a folder named Constants within your module project. In this class you will add definitions for your action names.
Note: ActionNames is the recommended name for the class. You can use a different name.

2. In the ActionNames class, add the following string constant. This constant is the name of an action that determines whether the application displays the MyView view.

public class ActionNames
{
         public const string ShowMyView = "ShowMyView";
}


To relate actions to sections of code:
1. Create a class named ModuleActions in your moduleproject. Add the following using statements to this class.
using Microsoft.Practices.CompositeUI;
Note: ModuleActions is the recommended name for the class. You can use a different name.

2. Add the implementation of business actions to the ModuleActions class. Create a method that shows the MyView view. Add the Action attribute to this method. The Action attribute specifies that the method is a business action that is to be registered in the action catalog for conditional execution.

public class ModuleActions
{
         private WorkItem _workItem = null; 

         [ServiceDependency]
         public WorkItem WorkItem
         {
                   get { return _workItem; }
                   set { _workItem = value; }
         } 

         [Action(Constants.ActionNames.ShowMyView)]
         public void ShowMyView(object caller, object target)
         {            
                  // Add code to show the view
          }
}


To add an action to the action catalog:
1. Add a new instance of the ModuleActions class to the Items collection of the WorkItem when your module loads. ObjectBuilder runs the action strategy when it constructs the ModuleActions object. The following code encapsulates the logic to create an instance of the ModuleActions class in a method named RegisterActions.

private void RegisterActions()
{
         rootWorkItem.Items.AddNew<ModuleActions>();
}


To conditionally execute an action using the action catalog:
1. Obtain a reference to the IActionCatalogService. You can implement a property as shown in the following code.

public IActionCatalogService ActionCatalogService
{
          get { return rootWorkItem.Services.Get<IActionCatalogService>(); }
}


2. Use the ActionCatalogService service to conditionally execute the ShowMyView action. Call the Execute (or CanExecute) method of the action catalog service and pass the action name, current WorkItem, a reference to the caller, and an optional fourth argument.

ActionCatalogService.Execute(Constants.ActionNames.ShowMyView, rootWorkItem, this, null);

Last edited Nov 30, 2007 at 9:17 PM by siacomuzzi, version 3

Comments

KjellSJ May 29, 2008 at 8:28 AM 
Must an action method be void? Is there any way to return a result from an action, e.g. invoke a service operation to get data?