» tagged pages
» logout
tag4sree
Return to Hibernate - Objects

Generic pipeline for business objects using Castle Windsor

Tags Applied to this Entry

1 person has tagged this page:

Currently I'm working with a system handling different operations when receiving business documents in object form. Its quite obvious that the code for handling this has evolved to have a lot more responsibilities than it was intended to.

So already having an Inversion of Control container in our stack I thought we could make the operations in the pipeline configurable using the container. To achieve this we need to specify an interface for the tasks in the pipeline, in our case its pretty simple:

public interface IPipelineTask<T>
{
    T Perform(T instance);
}

Secondly we need to define the pipeline itself, again using a generic interface:

public interface IPipeline<T>
{
    T Execute(T instance);
}

So basically what I want to accomplish is the ability to ask my Inversion of control container for a pipeline for a certain type and be able to call the Execute method on the instance and have all associated tasks performed on the instance I pass to the method. So the code for handling an incoming object of type Invoice would look like this:

Invoice incomingInvoice = ....;
ProjectContainer container = new ProjectContainer();
IPipeline<Invoice> pipeline = container.Resolve<IPipeline<Invoice>>();
Invoice processedInvoice = pipeline.Execute(incomingInvoice);
container.Release(pipeline);

So lets look at the implementation of the generic pipeline that handles executing of each task:

public class Pipeline<T> : IPipeline<T>
{
    private IPipelineTask<T>[] tasks;
    public Pipeline(IPipelineTask<T>[] pipelinetasks)
    {
        tasks = pipelinetasks;
    }

    public T Execute(T instance)
    {
        T processedInstance = instance;
        foreach (var task in tasks)
        {
            processedInstance = task.Perform(processedInstance);
        }
        return processedInstance;
    }
}

Note the constructor takes an array of IPipelineTasks<T> this is here our Inversion of Control container comes to play and does this for us using constructor dependency injection.

Each task needs to implement the IPipelineTask<T> with its specific business object type:

public class InvoiceNotifier : IPipelineTask<Invoice>
{
    private IUserNotifier usernotifier;
    public InvoiceNotifier(IUserNotifier notifier)
    {
        usernotifier = notifier;
    }

    public Invoice Perform(Invoice instance)
    {
        usernotifier.Notify(Notification.IncomingInvoice,
                            "The invoice with identifier " + instance.ID + " changed state to " + instance.State);
        return instance;
    }
}

Normally when working with arrays in Castle Windsor we have to specify each service to be part of the array in the configuration, however in our case we want all registered implementations of the specific interface to be passed to the pipeline. To do this we can add a custom sub dependency resolver to castle, in fact we can use the ArraySubDependencyResolver already written by Castle projects founder (and soon to be Microsoftee) Hammett. With this in place we can make the following configuration and our mission is accomplished and all tasks registered in the configuration will be injected when we request the generic Pipeline of this type.

<components>
   <!--Register the generic pipeline--> 
  <component
    id="bussiness.document.pipeline"
    service="Services.IPipeline`1, Services"
    type="Services.Pipeline`1, Services"
    lifetype="Thread"
    />
   <!--Register our pipeline tasks-->
  <component
    id="invoice.notification"
    service="Services.IPipelineTask`1[[Domain.Invoice, Domain]], Services"
    type="Services.InvoiceNotifier, Services"
    lifetype="Thread"
    />
  <component
    id="invoice.reciever"
    service="Services.IPipelineTask`1[[Domain.Invoice, Domain]], Services"
    type="Services.InvoiceReciever, Services"
    lifetype="Thread"
    />
  
   <!--Registration of services and repositories which our pipeline tasks uses-->
   ....
</components>

Using this infrastructure custom tasks can be specified easily in the configuration even from other namespaces and assemblies. And the pipeline doesn't need to worry about the dependencies of each Pipeline task, the container handles this

Username:
Password:
(or Cancel)