CAB: Injecting State into Child Work Items
February 18th, 2006
A common task that takes place when working with CAB, is the act of passing state information from a parent work item to a newly created child work item. You could simply use the default by reference passing in the .NET Framework, but this would generate references between parent and child work item, thus making them not reusable. To avoid this problem, a recommended practice is to pass copies of the State objects between them.
There are many possible ways of accomplishing this task. Let’s examine some of them, and analize their advantages and disadvantages.
Solution 1: The manual way
In this solution we will pass the state objects from the parent work item to the child work item after creating it.
First, in the parent work item, we create the child:
ChildWorkItem child = WorkItems.AddNew<ChildWorkItem>();
Then, we pass the state objects the child needs:
child.State["SomeInformation"] = this.State["SomeInformation"];
or we pass all the objects in the State bag:
foreach (string stateKey in this.State.Keys)
{
child.State[stateKey] = this.State[stateKey];
}
This solution is simple, but it creates repetitive code whenever you create a child work item and need the state to be passed down.
Important: If you need to set the ID property of the child work item, do it before passing the state objects. Setting the ID of a work item will cause its State bag to be cleared.
Solution 2: Using State Injection
CAB provides us of a State attribute used to indicate that the parent work item should automatically inject values into public properties of the child work item during the build process. This attribute can recieve a parameter indicating what item in the parent’s State bag we want to be injected. If it’s ommited, CAB will try to inject a value depending on the type of the property.
In this solution, we are going to use this attribute to have the state passed by injection.
An intuitive but wrong solution is described in the following code. Suppose we want the customer object to be automatically injected:
// In the child work item
// Indicate the parent work item to inject the Customer value from its State bag
[State("Customer")]
public Customer Customer
{
set
{
this.State["Customer"] = value;
}
get
{
return (Customer)this.State["Customer"];
}
}
The problem here is that the State bag of the child work item is not yet initialized at the moment of injecting the values.
When WorkItems.AddNew is called, the ObjectBuilder starts building the new work item. To create it, the ObjectBuilder uses a pipeline of strategies that allows multiple operations to be executed during the process of creation. This pipeline is divided in stages, and the default ones in the ObjectBuilder are PreCreation, Creation, Initialization and PostInitialization.
If we take a look at the strategies within the Initialization stage, we have:
- PropertySetterStrategy
- PropertyReflectionStrategy
- MethodReflectionStrategy
- MethodExecutionStrategy
These strategies are executed in order by the ObjectBuilder in the build process. During the PropertySetterStrategy, the values of the public properties with attributes are injected. In the previous code, the ObjectBuilder tries to inject the Customer property. But the problem is that the State bag of the child work item is not initialized until the MethodExecutionStrategy is executed. Thus, if you run the code you’ll get a null reference exception.
As a workaround, you could store the injected value in a local variable and then copy it to the child State bag after the work item is initialized:
// When this method is called the State bag is already initialized
protected override void OnInitialized()
{
base.OnInitialized();
this.State["Customer"] = _customer;
}
private Customer _customer;
[State("Customer")]
public Customer Customer
{
get
{
return _customer;
}
set
{
_customer = value;
}
}
The OnInitialized method is fired after the work item is initailzed, so at this point the State bag is ready to be used. The main drawbacks of this solution is that we have an extra local variable and the copy operation as an overhead.
Important 1: This solution won’t be usefull for you if you need to set the ID property of the child work item. The State bag is cleared when setting the ID property.
Important 2: In the CAB Help you will find a solution similar to the described in the wrong code. Be carefull, it does not work as expected.
Solution 3: Custom attribute and ObjectBuilder Strategy
Note: This is not implemented in CAB. See this post and this post for implementation.
This solution takes advantage of the extending capabilities of the ObjectBuilder. It consists in creating a custom attribute for work items (e.g: “InheritState”) which indicates that the State bag has to be copied from the parent work item on initialization. Then, a custom builder strategy would be in charge of accomplishing the copy operation at the end of the ObjectBuilder pipeline, after the initialization of the collections of the work items. This way, you would forget about injecting state into child work items:
// In the child work item
[InheritState]
public class ChildWorkItem : WorkItem
{
// …
}
And then, in the parent workitem, you just create the child workitem as in Solution 1:
ChildWorkItem child = WorkItems.AddNew<ChildWorkItem>();
As you can see, this solution is the most transparent and elegant. Besides, you could add parameters to the custom attribute to specify which state objects have to be copied.
For more information about creating custom strategies and attributes, have a look at the Hands On Lab N-8: ObjectBuilder available in the CAB GotDotNet workspace.
For implementation, see this post.

June 25th, 2007 at 1:47 pm
When I change the ID of the WorkItem apparently the State is not inherited.
Any idea ?
workItem = parentWorkItem.WorkItems.AddNew>(”Rechtoestand_” + context.Id);
workItem.ID = “Rechtoestand_” + context.Id;
workItem.Controller.Run();
July 14th, 2007 at 11:29 pm
Hi Lombaers,
When you set the ID property of a WorkItem, the state bag is cleared. I found this in the docs, regarding the WorkItem.ID property:
“Gets/sets the ID of this WorkItem. The ID is used for persistence of the WorkItem. By default, the ID will be a GUID. If you set a new ID, the old state data will be lost and replaced with new, empty state.”
The question that comes to my mind is why would I want to change the ID of a WorkItem? I use the ID to uniquely identify a WorkItem, therefore I always leave it unchanged.
Remember that if you want to set the ID of the WorkItem (for the first time) when inheriting the state, you can set the SetWorkItemID flag of the attribute to true.
Cheers,
Mariano Szklanny