Rich Newman

December 12, 2007

SCSF Business Modules: Start Up and the ControlledWorkItem (Introduction to CAB/SCSF Part 20)

Introduction

Part 19 of this series of articles discussed business modules in a Smart Client solution generated using the Smart Client Software Factory. This article continues that discussion.

The Load Method of a Business Module

As discussed in the previous article, a business module has a class called ‘Module’ which inherits from class ModuleInit. We saw in part 1 of this series of articles that this means the Load method in that class will get called at start up, provided the module has been added to the ProfileCatalog file.

The Load method of Module generated by the Smart Client Software Factory is as below:

        public override void Load()
        {
            base.Load();
 
            ControlledWorkItem<ModuleController> workItem = _rootWorkItem.WorkItems.AddNew<ControlledWorkItem<ModuleController>>();
            workItem.Controller.Run();
        }

As we can see, it’s creating a ControlledWorkItem class instance and adding it to the WorkItems collection of the root WorkItem. It’s then calling the Run method on the Controller property of this WorkItem.

ControlledWorkItem

ControlledWorkItem is a class that inherits directly from WorkItem. So a ControlledWorkItem is a WorkItem. The ControlledWorkItem also adds additional functionality to the WorkItem, and, crucially, it is a sealed class (which means we can’t inherit from it).

The idea here is that each business module should have a ControlledWorkItem as a root for its functionality. This is what we are creating in the Load method. In the overall WorkItem hierarchy each business module ControlledWorkItem is immediately below the root WorkItem for the entire solution.

Inheriting WorkItem to add Functionality

The ControlledWorkItem has been created to clarify the situation with regard to adding code to WorkItems. When we start using the CAB we quickly find that we need our WorkItems to be extended in various ways. They are intended to control business use cases, after all. For example we may want specific services instantiated at start up and added to the Services collection. Doing this in the WorkItem itself may seem like a sensible thing to do. Clearly the main WorkItem class is a CAB framework class, but we can inherit from it to give it this additional behaviour.

The reference implementations of both the CAB and the SCSF do this: each WorkItem inherits from the base WorkItem class and extend it to give the use case functionality. If you look at the CustomerWorkItem in the Bank Teller Reference Implementation you’ll see this.

Why Inheriting from WorkItem has been Deprecated

The difficulty with this is that our WorkItem class is acting as both a container for all the various WorkItem collections, as we have discussed before, AND as a place where all the code for a business use case goes.

This breaks the Single Responsibility principle, which is that every class should have just one responsibility in a system to avoid confusion.

As a result the Patterns and Practices team have decided it’s not ideal to have developers inherit from WorkItem and add functionality to the derived class. Instead a second class is created to contain the new code, and that class is associated with the WorkItem class by composition.

How ControlledWorkItem Addresses the Problem

This is what the ControlledWorkItem is doing. The ControlledWorkItem class itself inherits from WorkItem, but also has a member variable that references another class. The type of this class is generic (so the developer provides it), and the class is instantiated when the ControlledWorkItem is created.

So in the line of code below we are creating the ControlledWorkItem and adding it to the root WorkItem’s WorkItems collection. However we are also telling the ControlledWorkItem that its member class should be of type ModuleController, and that class will get instantiated and set up as the member variable.

ControlledWorkItem<ModuleController> workItem = _rootWorkItem.WorkItems.AddNew<ControlledWorkItem<ModuleController>>();

We are not expected to inherit from ControlledWorkItem itself. In fact we can’t because it is sealed: the Patterns and Practices team have done this deliberately to indicate that the pattern has changed. Instead we add our additional functionality for the WorkItem to the ModuleController class.

ModuleController

We can access the ModuleController instance from the ControlledWorkItem using the Controller property. We can then call a Run method on that class. This is the standard pattern that is generated by the Guidance Automation Package: note that the final line in the Load method above is:

workItem.Controller.Run();

So we can add start up code for the WorkItem into the ModuleController class in the Run routine.

The SCSF gives us a default ModuleController whenever we set up a Module, as we have seen. This has a default Run method. There isn’t any code that actually does anything in this method, but four empty methods are set up in ModuleController to indicate to us the sort of things we should be doing:

    public class ModuleController : WorkItemController
    {
        public override void Run()
        {
            AddServices();
            ExtendMenu();
            ExtendToolStrip();
            AddViews();
        }
...

There are also comments in these routines to describe what we should be doing inthem. To see this in more detail look in any of the ModuleController classes in the sample code.

WorkItemController Class

Note also above that our default ModuleController inherits from a class called WorkItemController, which is an abstract base class intended to be used just of these controllers. Inheriting from this ensures that we have a Run method in our derived class as there is an abstract function of this name in the base class.

The base WorkItemController also gets a reference to the associated WorkItem using our usual dependency injection pattern. This can be accessed via the WorkItem property on the WorkItemController class.

Finally the WorkItemController class has two overloaded ShowViewInWorkspace methods, which can create and show a SmartPart in a named Workspace in the WorkItem.

Obviously we don’t have to make our ModuleController inherit from WorkItemController. However, if we don’t this base class functionality will not be available.

Conclusion

This article has discussed the standard patterns generated by the Smart Client Software Factory for starting up business (and other) modules.

Part 21 of this series of articles will look briefly at foundational modules, and will also discuss the way names are handling in Smart Client Software Factory projects.

The Shocking Blue Green Theme. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 81 other followers