Showing posts with label SCSF. Show all posts
Showing posts with label SCSF. Show all posts

Friday, July 04, 2008

CAB/SCSF: Designing WorkItems From Use Cases

One of the most elusive and complicated parts of CAB is the workitem dependency injection container and all its use case resource collections. SCSF improved on the CAB workitem by adding the WorkItemController as the place to put use case controller logic. The WorkItemController gets a WorkItem instance injected, and many developers never use anything but this default workitem built into the ModuleController. This might lead to a system that is hard to build and maintain due to lack of task isolation because of no good mechanisms for controlling the lifespan of use cases (workitems).

In most projects there is a strong focus on the visual elements of the solution: the views (WorkSpaces, SmartParts, Ribbons, etc) - and CAB makes it quite easy to create a composite UI application. But this doesn't necessarily make for a maintainable, pluggable and loosely coupled composite application / service-oriented business application (SOBA).

It is imperative that the workitem structure and lifespans are analyzed to be able to design the lifecycle management based on the dynamic behavior, events and state of the system's use cases. Without designing a set of workitems and their lifecycle management requirements, your application will not be able to provide users with a task-oriented composite application allowing users to start, work on, switch between, and complete a set of use cases. Without workitems, your system will be just a multi-view forms-over-data application - having no processes for resource life-cycles.

I recommend reading the 'Identifying WorkItems' section of Designing Smart Clients based on CAB and SCSF by Mario Szpuszta, a case-study from Raiffeisen Bank. The case-study shows how to analyze use case diagrams to identify workitems and modules, and provides some good rules for how to do this analysis. It shows how to use root use-cases and pure sub use-cases to identify first-level workitems and sub-workitems, and what I call hub-workitems from sub use-cases that are used from several use-cases. The hub-workitems are often entry points to use-cases such as "find customer".

What I would like to add to the procedure is that adding a dynamic modeling aspect based on the use cases will help with explaining, doing and presenting the workitem analysis.

Dynamic modelling is done using UML activity diagrams or BPMN diagrams, both showing workflow and responsible parties using swimlanes. The main help of using these dynamic diagrams is to get a dynamic (run-time) view of the static use case diagrams to be able to see the lifespan of tasks.

Create business process or workflow diagrams of the use cases. Each process will have a defined start and end, and is a good candidate for becoming a first-level workitem with zero, one or more sub-workitems. The diagram swimlanes are good candidates for identifying sub-workitems of the first-level workitem. Use the process diagrams to identitfy the lifecycle management requirements for workitems in your composite application.

Don't create a CAB application with only one workitem that lives as long as its module, unless your use case is as simple as that.

Friday, June 20, 2008

CAB/SCSF: View Parameters

CAB/SCSF use the wellknown Model-View-Presenter design pattern for its views. There is, however, a design flaw in its implementation: you cannot pass parameters to the view constructor. As documenten in the SCSF knowledge base, the proven practice is to pass the parameters using the WorkItem State collection.

This proven practice can be improved using generics to become type-safe, while still adhering to the MVP design rules. Add the following new method to the WorkItemController base class in the Infrastructure.Interface project:

public virtual TView ShowViewInWorkspace<TView, TParams>(string viewId, string workspaceName, TParams viewParams)
where TParams : class
{
string stateKey = StateItemNames.PresenterConstructorParams + typeof(TView).FullName + ":" + typeof(TParams).FullName;
_workItem.State[stateKey] = viewParams;

TView view = ShowViewInWorkspace<TView>(viewId, workspaceName);
return view;
}

Then add the following new method to the Presenter base class in the Infrastructure.Interface project:

protected TParams ViewParameters<TParams>() where TParams : class
{
//NOTE: must use typeof(view instance) to get correct run-time type of generic view type
string stateKey = StateItemNames.PresenterConstructorParams + _view.GetType().FullName + ":" + typeof(TParams).FullName;
return (TParams)_workItem.State[stateKey];
}

Now you can simply pass the parameters from the module controller to the view using ShowViewInWorkspace and then get the parameters in the presenter's OnViewReady event:

_viewParameters = ViewParameters<ViewParameters>();
if (_viewParameters == null)
{
throw new ArgumentException("Required view parameter error", "ViewParameters", null);
}

This little design improvement hides the gory details of using the untyped object State collection, e.g. generating shared keys and type casting.

Wednesday, May 07, 2008

Design Rules for Model-View-Presenter

In my current project the MVP pattern is used in the supervising controller mode. The MVP pattern is an adaption of the old MVC pattern that incorporates that the capabilities of WinForms views have become smart enough to lift some of the burdens previously implemented in the controller. This applies to e.g. handling click events and data-binding; a presenter only injects the model into the view which exploits data-binding, while a controller explicitly sets the values of each control in the view. In short, a presenter should handle the use-case logic only, not the view logic.

Each view must implement an interface that defines all interactions that the presenter has with the view. This ensures a clear separation between the presenter and the view, and has the advantage of making the presenter easy to unit-test using mocking. The view never calls other modules and services directly; it must always use an event to request that the presenter provides the needed data or service.

This project uses CAB/SCSF to generate the view modules. The generated code is not quite as I would like it, I prefer that the view has no knowledge of the presenter and no direct access to it either as this gives a cleaner separation between views and presenters. I prefer that the view interface defines a set of events that is used to communicate view events to the presenter. This makes it harder for unknowledgable programmers and code monkeys to misuse the presenter reference; making shortcuts and ignoring the reasons for using patterns is very common and leads to unmaintainable code. Read Jeremy Miller's View to Presenter Communication for further opinions on this topic.

The following MVP design rules have a CAB flair, but are generally applicable to any MVP implementation:

1. All views should have a XxxView suffix: TaskView/ITaskView
2. All presenters should have a XxxPresenter suffix: TaskViewPresenter
3. Let the presenter do all use-case processing, but keep GUI control details in the view
4. All presenter methods called by the view must start with OnXxx() as they are events by design (MVP)
5. Calls from the view to the presenter should be kept at an absolute minimum, and used for "event" type calls only: _presenter.OnViewReady();
6. It is FORBIDDEN to use the presenter reference to access the model or services directly, no return values from presenter methods
7. Calls from the presenter to the view MUST go via the interface only
8. No methods in the view shall be public unless they are defined in the interface
9. It is FORBIDDEN to access the view from anywhere but the presenter, except for loading and showing the view from the CAB ModuleController
10. The interface methods shall have long meaningful names (not "SetDataSource") based on the domain language of the use-case
11. The interface should contain methods only, no properties - afterall the presenter drives the use-case by calling methods, not by setting data
12. All data in the MVP component should be kept in the model, no data should exist only as properties of UI controls
13. The methods of the view interface should not contain UI control name references (e.g. AddExplorerBarGroup) as this makes the presenter know too much about the implementation technology of the view
14. Stick with domain naming in the view methods (e.g. AddTaskGroupHeader), this makes the code easier to understand and the tests self-describing

Rule 11 and 12 might surprise you if your perception of the model is that it contains business entities only - traditional database-driven design thinking. The model is rather business process documents that are involved in the use-case, i.e. domain entity and value objects. Even views used for searching have a model, it is a specification or query object. Jeremy has some guidelines on assigning responsibilities in MVP.

Rule 6 helps adhering to the "tell, don't ask" principle - properties make it far too easy to ask, rather use methods on the view to tell it what to do. I've seen design-time view data-binding directly against presenter properties, not exactly separation of concerns. Neither is it very testable, as there is no interaction from the presenter through the view's interface when the view asks the presenter directly. When mocking the view, there will be no recorded / testable interaction.

Using long meaningful names in the view interface has the nice side-effect of making the presenter unit-tests a kind of mini documentation.

I wish there was a FxCop Contrib project on CodePlex like for EntLib, CAB/SCSF and ASP.NET MVC. Then maybe I would write and publish some of these MVP design rules as custom FxCop rules.

Friday, May 02, 2008

SCSF Dependency Injection Container, CAB WorkItem

Getting started with CAB/SCSF can be a bit daunting, and this article is a summary of the most important aspects regarding the mysterious CAB workitem. I've found this to be a good intro to CAB, start with part 18: http://richnewman.wordpress.com/intro-to-cab-toc/

The CAB workitem is simply a dependency injection container, even if "wrongly" promoted as an implementation of a use case. A use case contains no UI-details or service implementation references, but of course these artifacts are needed to realize a use case in an application. The workitem is a container of resources needed to implement a use case.

A DI-container is a collection of resources than can be located using a resource locator that is used to resolve resource injection requests at run-time. The resources can be loaded into the container either dynamically or through XML config. The workitem DI-container has several collections of resources, which can be quite confusing. A workitem has these collections:


  • Items: all objects added to any of the child collections including this, except Workspace.SmartParts; independent of type of resource.
  • WorkItems: child WorkItem objects, must inherit CAB.WorkItem.
  • Services: any object added as a service. NOTE: the container can only contain one instance of each service type.
  • Workspaces: encapsulate a particular visual layout of controls and SmartParts, such as within tabbed pages.
  • SmartParts: any object marked with [SmartPart] in the class definition, do not have to be a visual component.
  • Workspace.SmartParts: visual components shown in the workspace, must inherit System.Windows.Forms.Control. NOTE: these items are not added to the .Items or .SmartParts collections on the workitem.
In addition, the workitem DI-containers can be nested, i.e. a workitem can contain other workitems.

When the DI-container tries to resolve a [ServiceDependency] injection or just Get<IService> for a service, it looks in the nested set of containers from the current workitem and up through the parent workitems, including the root workitem to find the service. The service locator does not look through the set of child workitems. Thus, shared services should be placed towards the root or in the root workitem for the service locator to find them.


One aspect of the workitem Services collection that most CAB developers seems to have missed, is that even if you can add only one instance of a service to a workitem, you still can add separate instances of the same service to child workitems. The service locator will find the nearest service in the tree towards the root. E.g. the ActionCatalog service can be added to several ModuleController first-level workitems, in addition to the root workitem. We have used this feature to add several scope-based services such as a SharedModelCacheSevice and a RibbonControllerService at different levels of the workitem structure. This way, the same service can have different scopes in a CAB solution, dependent on where it is injected. The scope controls how workitem resources are located by the resource locator, e.g. where the locator looks for Services, Commands and UIExtensionSites.

Note that the resource locator strategy only applies to these workitem collections: Services, Workspaces, Commands, EventTopics, UIExtensionSites.

When the SmartClient application starts, it will create the root workitem and add a set of CAB services to it:

public ... class SmartClientApplication<TWorkItem, TShell> ...
{
protected override void AddServices()
{
RootWorkItem.Services.AddNew <ProfileCatalogModuleInfoStore, IModuleInfoStore>();
RootWorkItem.Services.AddNew <WorkspaceLocatorService, IWorkspaceLocatorService>();
RootWorkItem.Services.AddNew <XmlStreamDependentModuleEnumerator, IModuleEnumerator>();
RootWorkItem.Services.AddNew <DependentModuleLoaderService, IModuleLoaderService>();
RootWorkItem.Services.AddOnDemand <ActionCatalogService, IActionCatalogService>();
RootWorkItem.Services.AddOnDemand <EntityTranslatorService, IEntityTranslatorService>();
}
}

These standard CAB services are available from all over CAB as they are added to the root workitem that becomes the ultimate parent of all other workitems loaded into the SmartClient application.

In addition to the standard set of CAB services, the custom CAB modules that get loaded will typically add extra CAB services into the root workitem. SCSF will load CAB modules as defined in ProfileCatalog.XML and this will trigger the CAB module initialization mechanism filling the workitem collections and followed by the DI-container resolving.

What adds to the somewhat complicated workitem collections mechanism is that several of these CAB services themselves are collections of other shared objects. E.g. the EntityTranslatorService is a collection of translators that can be added to and located within that service. Note that the DI-container only performs the resource resolving and dependency injection on objects that are added directly to a workitem collection. Thus, objects not added directly to a workitem collection will not get any DI magic performed on them. E.g. if you create a service agent from within a CAB service to call a web-service, that agent will not be subject to any DI resolving. This can be very confusing when [ServiceDependency] objects are not injected as planned, thus be very careful when designing your solution.

NOTE: do not try to access resources that must be resolved by the DI-container, from within class constructors, as the resource may not have been injected yet. Use the [InjectionConstructor] attribute on your constructor if you need to force the resource locator to resolve and inject services that you need.

Using the DI-Container from CAB Modules

CAB Services are usually implemented using CAB foundation modules, but can also be implemented using CAB business modules. The former module type is applicable for shared CAB services and components, such as modules that provide access to web-services; while the latter module type is suitable for use-case specific CAB components. Note that both types of resources are "services" in CAB as they are added to the workitem Services collection. This overloading of the term "service" should not make you confuse CAB service modules with WCF service gateways, even if the CAB service used the WCF service.

Adding CAB services from CAB modules

A foundation module is a bare-bones module (the original CAB module) as it is just for implementing a container for shared components.

A business module is a module (new in SCSF, extending the original CAB module) enhanced with the application controller pattern as it for implementing a container for use-case specific components. The SCSF module adds the
ControlledWorkItem to separate the use-case related code from the workitem container related code (in CAB you had to put the use-case controller code in the workitem class, mixing the DI-container logic with the use-case logic).

So even if you could implement an AddServices method in the ModuleController and call it from its Run method, I would recommended that you still add module services from Module.AddServices method. This keeps the container related code separated from the use-case related code:

public override void AddServices()
{
//NOTE: must add UserAccessClaimSet first, as NavigationService depends on this service
WorkItem.RootWorkItem.Services.AddNew <UserAccessClaimSet, IUserAccessClaimSet>();
WorkItem.RootWorkItem.Services.AddNew <NavigationService, INavigationService>();
}

Prefer adding services like this over the other mechanisms provided by SCSF (
app.config / [Service] attribute).

Resolving and using CAB services


To be able to use the CAB services they must be gotten from the workitem DI-container. There are two ways of doing this, using the service collection Get<IService> method or using dependency injection with the [ServiceDependency] attribute. The DI way is preferred as the class using the service can then get the service reference once and use it multiple places. The Get<IService> way has the advantage that it can be called with a bool EnsureExists parameter that will throw an exception if the service is not (yet) registered in the workitem DI-container.

Use the [ServiceDependency] attribute either to inject a service into a class property or in the class c'tor:


[ServiceDependency]
public IUserAccessClaimSet Service
{set { _service = value; }}

[InjectionConstructor]
public NavigationService([ServiceDependency] IUserAccessClaimSet userAccessClaimSet)
{
_userAccessClaimSet = userAccessClaimSet;
}

Getting the service directly is more explicit and allows for using the EnsureExists parameter:

IUserAccessClaimSet costOfGasService = WorkItem.Services.Get<IUserAccessClaimSet>(true);

Without the EnsureExists parameter, the returned service can be null and your code will then not throw an exception when getting the service, but rather on the first service invokation later on, which can make it harder to diagnose the actual cause of the failure. Using the [ServiceDependency] attribute has the same weakness, and this is a common cause of CAB perplexity.
Still, I would recommend using the [ServiceDependency] attribute.



Using the DI-container from CAB views

All CAB views exist within a CAB business module and must thus use one of the recommended methods for resolving service dependencies, preferring the [ServiceDependency] attribute. CAB views are designed using the 'supervising controller' MVP pattern, and the presenter object is added to the workitem Items collection like this:

_presenter = _workitem.Items.AddNew<TaskViewPresenter>();

Adding it to the Items collection of the workitem DI-container triggers the dependency injection resolver.

Tuesday, April 15, 2008

WCF+SCSF: Client Domain Objects, Mapper Pattern

The client domain objects (aka business entity - BE) must be related to the data contracts provided by the web-services. Still, they will need to be enhanced with e.g. dirty-tracking, validation, state, property conversions (xs:date), business rules, and other aspects needed to produce a fully working client application. This article describes how to achieve this when sharing domain objects across layers is not an option (i.e. interop with JEE services), and the same applies to AOP.

One option for implementing the BE objects, described in this article, is to apply the Mapper pattern to the WSDL scema-based DTO objects to transform them into domain objects. Read ‘Design of Client Domain Objects’ for an intro this option and the related Decorator option.

Service Proxy

The mapper option has no special requirements for the service proxy or its message and data contracts, as there are no shared objects between the proxy and the business entities, i.e. the proxy is totally self-contained. The mapper objects do all transformation to/from the DTO objects and the BE objects.

CAB EntityTranslatorService, CAB EntityMapperTranslator

The EntityTranslatorService class is a service that provides a registry of translators and translation services between two classes. The user must implement the translators and register them with the service.

For more information see: ms-help://MS.VSCC.v80/MS.VSIPCC.v80/ms.practices.scsf.2007may/SCSF/html/03-01-090-How_to_Translate_Between_Business_Entities_and_Service_Entities.htm

The EntityMapperTranslator class is a base helper implementation of an IEntityTranslator that provides placeholders to translate from business entities to service and viceversa.


For more information see: ms-help://MS.VSCC.v80/MS.VSIPCC.v80/ms.practices.scsf.2007may/SCSF/html/03-01-090-How_to_Translate_Between_Business_Entities_and_Service_Entities.htm

Mappers and the Translator Service

A mapper must be derived from the CAB class EntityMapperTranslator<BE, DTO> and implement two methods:

DTO = BusinessToService(BE)
BE = ServiceToBusiness(DTO)

A mapper must traverse the complete object graph to translate all child collections and objects when the mapped object is a DDD "aggregate root".

Always add a mapper for collections of the mapped DTO and BE objects. A list mapper must iterate the collection and use the applicable object translator to map each item in the list.

Each module must register its own mappers with the CAB translator service from the CAB module AddServices:

public override void AddServices()
{
AddEntityTranslators();
_workItem.RootWorkItem.Services.AddNew<MyService, IMyService>();

}

private void AddEntityTranslators()

{
IEntityTranslatorService translator = _workItem.RootWorkItem.Services.Get<IEntityTranslatorService>();

translator.RegisterEntityTranslator(new MyTranslator());
translator.RegisterEntityTranslator(new MyListTranslator());

. . .
}

The mappers are used by the service agents to map DTO objects to BE objects and vice verca. Each CAB service that depends on a service agent must have a [ServiceDependency] on IEntityTranslatorService:

[InjectionConstructor]
public MyService([ServiceDependency]IEntityTranslatorService translator)
{
_translator = translator;
_myServiceAgent = new MyServiceAgent(_translator);
}

MyService is a CAB service and will thus get the EntityTranslatorService injected by the DI-container. It then creates the required service agents using the translator service reference. MyServiceAgent is not a CAB service, thus it is not subject to any DI-container dependency resolving magic.

Service Agents Perform the Mapping

The service agents use the EntityTranslatorService to map the DTO objects returned by the service proxy into BE objects:

AllocationTypeList allocationTypeList;
NominationTypeList nominationTypeList = _proxy.GetNominationAndAllocation (periodType, out allocationTypeList);

nominationList = _translator.Translate <List<Nomination>>(nominationTypeList);
allocationList = _translator.Translate <List<Allocation>>(allocationTypeList);

The reverse mapping must be performed on all BE objects passed as DTO objects into the service proxies.

NEVER LET A DTO OBJECT BE USED OUTSIDE SERVICE AGENTS.

Mapping from DTO to BE

Your BE class must have a constructor to initialize the object for normal use. The constructor must initialize all properties that must have a defined initial value. In addition, the c'tor must initialize all child objects that are exposed as public properties, to avoid null reference exceptions when using the BE (make it simple to use the BE). This also applies to child collections; create the collection to make it ready for use. Don't break the DRY principle by adding the initialization code multiple places.

public Nomination()
{
//initialize all contained valueobjects with members
this.NominatedValueField = new UnitValueType();
this.NominatedValueField.Unit = String.Empty;
. . .
}

The mapper code will create a new instance of the BE class and initialize it based on values from the DTO object. This can involve the whole specter from simple value copying to complex conversion logic.

Mapping from BE to DTO

Your mapper class must initialize the created DTO object before using it. The mapper must initialize all properties that must have a defined initial value. In addition, the mapper must initialize all child objects that are exposed as (de-normalized) public properties, to avoid null reference exceptions when mapping the BE. This also applies to child collections; create the collection to make it ready for use.

//initialize all normalized valueobjects with members
dto.NominatedValueField = new UnitValueType();
dto.NominatedValueField.Unit = String.Empty;

Also initialize all DTO fields that are required by the XSD schema, failure to initialize these fields will give serialization errors:

[DataMemberAttribute(IsRequired=true, . . .)]

The mapper code will create a new instance of the DTO class and initialize it based on values from the BE object. This can involve the whole specter from simple value copying to complex conversion logic.

Converting XML Data Types to/from Strongly Typed Properties

Note that xs:date becomes string in .NET as there is no Date only type, just DateTime. DO NOT LET THIS KIND OF WEAK TYPE DESIGN LEAK INTO YOUR BE OBJECTS. Contain the weak typing in your DTO object by converting to/from strongly typed properties in the mapper.

dto.DemandDate = value.DemandDate.ToString(WSDateFormat);

entity.DemandDate = DateTime.ParseExact(value.DemandDate, WSDateFormat, null);

This keeps your BE object within the "Simple vs Easy" principle.

Dirty Tracking

If your BE implements INotifyPropertyChanged to enable data-binding, then the event is a good place to add dirty-tracking: Simple Dirty Tracking in Domain Objects.

Tuesday, April 08, 2008

CAB/SCSF: Unit Testing Presenters, Views, Events

Unit-testing a CAB/SCSF module and all its presenters and views, in addition to testing the publish/subscribe event handling, can be quite challenging to get started with. The TestableRootWorkItem class is required to get started with unit-testing and mocking, as this does all the dependency injection that CAB depends on. Just add all the components to the WorkItem to trigger the DI mechanism.

Use the TestableRootWorkItem like this:

private MockRepository _mocks;
private TestableRootWorkItem _workitem;
private MyViewPresenter _presenter;
private IMyView _view;
private IMyService _service;

[TestFixtureSetUp]
public void Initialize()
{
_mocks = new MockRepository();
_service = _mocks.CreateMock<IMyService>();
_view = _mocks.CreateMock<IMyView>();

_workitem = new TestableRootWorkItem();
_workitem.Services.Add(_service);

//perform the CAB DI-container magic
_presenter = _workitem.Items.AddNew<MyViewPresenter>();
_presenter.View = _view;
}


Note: do not try to access the workitem or any injected dependencies such as [ServiceDependency] inside constructors, things might not have been resolved by the DI container yet. This especially applies to stuff from base classes and things that are not resolved using an [InjectionConstructor].

When testing that event publishing actually triggers the subscribers, you can add fake event publishers and subscribers to help with the mocking. In addition, your unit-test should verify that the raised event causes presenters and views to be loaded by checking the content of the Items and SmartPart collections afterwards.

Implement your CAB/SCSF event-based unit-test like this:

[TestFixture]
public class ModuleTestFixture
{
. . .

[TestFixtureSetUp]
public void Initialize()
{
_workitem = new TestableRootWorkItem();
_eventPublisher = _workitem.Items.AddNew<MockEventPublisher>(_eventPublisherId);
_eventSubscriber = _workitem.Items.AddNew<MockEventSubscriber>(_eventSubscriberId);
_moduleInitializer = new Module(_workitem);
_moduleInitializer.Load();
}

[Test]
public void ShouldLaunchPriceConfigViewOnEvent()
{
int itemPresentersCount = FindItemsByTypeRecursive <PriceConfigurationViewPresenter>(_workitem);
Assert.AreEqual(0, itemPresentersCount);

int smartPartViewsCount = FindSmarPartsByTypeRecursive <PriceConfigurationView>(_workitem);
Assert.AreEqual(0, smartPartViewsCount);

_eventPublisher.OnNewOrder(new NewOrderEventArgs(123, Constants.OperationNames.StartPriceConfigurationView));


Assert.AreEqual(true, _eventPublisher.HasRaisedNewOrder, "EventPublication <NewOrder> failed");
Assert.AreEqual(true, _eventSubscriber.HasHandledNewOrder, "EventSubscription <NewOrder> failed");

itemPresentersCount = FindItemsByTypeRecursive <PriceConfigurationViewPresenter>(_workitem);
Assert.AreEqual(1, itemPresentersCount);

smartPartViewsCount = FindSmarPartsByTypeRecursive <PriceConfigurationView>(_workitem);
Assert.AreEqual(1, smartPartViewsCount);
}


private int FindItemsByTypeRecursive<T>(WorkItem workItem)
{
ICollection<T> items = workItem.Items.FindByType<T>();
int typeCount = items.Count;
foreach (System.Collections.Generic.KeyValuePair<string, WorkItem> item in workItem.WorkItems)
{
typeCount += FindItemsByTypeRecursive<T>(item.Value);
}
return typeCount;
}


private int FindSmarPartsByTypeRecursive<T>(WorkItem workItem)
{
ICollection<T> items = workItem.SmartParts.FindByType<T>();
int typeCount = items.Count;
foreach (System.Collections.Generic.KeyValuePair<string, WorkItem> item in workItem.WorkItems)
{
typeCount += FindSmarPartsByTypeRecursive<T>(item.Value);
}
return typeCount;
}

}


public class MockEventPublisher
{
[EventPublication(Constants.EventTopicNames.NewOrder, PublicationScope.Global)]
public event System.EventHandler<NewOrderEventArgs> NewOrder;

public virtual void OnNewOrder(NewOrderEventArgs eventArgs)
{
if (NewOrder != null)
{
_hasRaisedNewOrder = true;
NewOrder(this, eventArgs);
}
}

public bool HasRaisedNewOrder
{
get { return _hasRaisedNewOrder; }
set { _hasRaisedNewOrder = value; }
}
private bool _hasRaisedNewOrder = false;
}


public class MockEventSubscriber
{
[EventSubscription(Constants.EventTopicNames.NewOrder)]
public void OnNewOrder(object sender, NewOrderEventArgs eventArgs)
{
if (eventArgs.OperationName == Constants.OperationNames.StartPriceConfigurationView)
{
_hasHandledNewOrder = true;
}
}

public bool HasHandledNewOrder
{
get { return _hasHandledNewOrder; }
set { _hasHandledNewOrder = value; }
}
private bool _hasHandledNewOrder = false;
}

If you have issues with understanding that a WorkItem is just a dependency injection container or what all the different collections such as Items, Services, SmartParts, WorkSpaces, etc are for; I recommend reading Rich Newman's Introduction to CAB/SCSF. Read part 18 first.