I have moved my active blog over to tumblr. I've maintained this blog for reference but will be posting to http://www.robustsoftware.co.uk instead. I've pointed my Feedburner feed to tumblr so if you're subscribed already you should already have switched with me.

A better ActionResult: Open Rasta edition

I’ve been meaning to blog more about my experiences with Open Rasta but haven’t had a sufficiently focused topic to work with so far. However, whilst reading Jimmy Bogard’s post on a better ActionResult I thought doing similar would be equally possible with Open Rasta. Go off and read Jimmy’s post if you haven’t already because I’m going to assume you have.

First, some things to know about Open Rasta. It handles requests by passing them through a pipeline. Now this isn’t some mythical process that you have to spend hours working out as with MVC. It is instead made up of pipeline contributors which you register as part of your configuration. To help you debug Open Rasta prints out all the contributors in the order they’ll be executed for you during initialisation. Another difference in Open Rasta is what things are called, controllers are handlers, actions are operations, action results are operation results and models are resources. Their function is similar enough to require no further explanation in the context of this post.

Open Rasta gives you about 15 contributors to form the basis of request handling which you can replace or add to as you see fit. What we will do in order to emulate Jimmy’s code is create a new contributor and insert it into the pipeline. These are pretty simple to create and register, here’s all the code for our custom pipeline contributor:

public class CommandOperationResultInvokerContributor : IPipelineContributor

{

    readonly IDependencyResolver resolver;

 

    public CommandOperationResultInvokerContributor(IDependencyResolver resolver)

    {

        this.resolver = resolver;

    }

 

    public void Initialize(IPipeline pipelineRunner)

    {

        pipelineRunner.Notify(ExecuteCommand)

            .After<KnownStages.IOperationExecution>()

            .And.Before<KnownStages.IOperationResultInvocation>();

    }

 

    PipelineContinuation ExecuteCommand(ICommunicationContext context)

    {

        if (!(context.OperationResult is CommandOperationResult))

        {

            return PipelineContinuation.Continue;

        }

 

        var commandOperationResultType = context.OperationResult.GetType();

 

        resolver.AddDependencyInstance(commandOperationResultType,

            context.OperationResult, DependencyLifetime.PerRequest);

 

        var commandHandlerType = typeof(CommandOperationResultHandler<>)

            .MakeGenericType(commandOperationResultType);

 

        var commandHandler = (ICommandOperationResultHandler)

            resolver.Resolve(commandHandlerType);

 

        context.OperationResult = commandHandler.Execute();

 

        return PipelineContinuation.Continue;

    }

}

The Initialize method is called during initialisation so that Open Rasta can determine where in the pipeline you want your contributor to be invoked. The KnownStages class contains a bunch of interface aliases for significant stages in the standard pipeline allowing you to latch onto them without tying you to an implementation.

KnownStages.IOperationExecution is the Open Rasta equivalent of invoking the controller action and KnownStages.IOperationResultInvocation is the the equivalent of executing the ActionResult produced by the action. We’re slipping in between these two steps so we can execute our command handlers when needed; allowing us to change the OperationResult before it is executed.

So what do we actually do when this pipeline contributor gets invoked?

First we check if the OperationResult attached to the ICommunicationContext is of the CommandOperationResult type. This is identical to checking whether the ActionResult is of the BetterActionResult type in Jimmy’s post. If it isn’t we exit our pipeline contributor, telling Open Rasta to carry on to the next stage in the pipeline.

public abstract class CommandOperationResult : OperationResult

{

}

If it is a CommandOperationResult we retrieve the type of the current OperationResult and use it to register the OperationResult with our dependency resolver for the lifetime of the current request. The reason we do this is to be able to take the OperationResult as a dependency of the command handler. I’m not sure if this is any better but it does avoid using reflection to execute the command handler.

As a side note, Open Rasta uses an IDependencyResolver interface which is used throughout the codebase. It ships with it’s own implementation; I know there’s an implementation for Ninject and I think there’s one for StructureMap knocking around somewhere. I’ve only used the internal implementation as it does everything I’ve needed so far.

We then work out the type of CommandOperationResultHandler we’ll need to handle this command and use it to retrieve an instance from the dependency resolver. Again, this is very similar to Jimmy’s code apart from the fact that we cast the result as an ICommandOperationResultHandler. This is the second of the things that lets us avoid reflection.

public interface ICommandOperationResultHandler

{

    OperationResult Execute();

}

 

public abstract class CommandOperationResultHandler<T>

    : ICommandOperationResultHandler

{

    protected readonly T command;

 

    protected CommandOperationResultHandler(T command)

    {

        this.command = command;

    }

 

    public abstract OperationResult Execute();

}

We execute the command handler, which will have taken the CommandOperationResult as a dependency due to us registering it earlier. It could also take the ICommunicationContext as a dependency as Open Rasta registers that for you, as it does for IRequest, IResponse and the other things that make up ICommunicationContext.

The result of executing the command handler is set as the OperationResult of the current ICommunicationContext, this encourages you to swap out the CommandOperationResult for one of the standard OperationResult objects which will then get handled as normal by the next stage in the pipeline, KnownStages.IOperationResultInvocation.

Here’s my equivalent of Jimmy’s DeleteRequestResult and DeleteRequestResultInvoker:

public class DeleteCommand<T> : CommandOperationResult

{

    public DeleteCommand(T resource)

    {

        Resource = resource;

    }

 

    public T Resource { get; private set; }

}

 

public class DeleteCommandHandler<T>

    : CommandOperationResultHandler<DeleteCommand<T>>

{

    readonly ISession session;

    readonly ILogger logger;

 

    public DeleteCommandHandler(

        DeleteCommand<T> command, ISession session, ILogger logger)

        : base(command)

    {

        this.session = session;

        this.logger = logger;

    }

 

    public override OperationResult Execute()

    {

        session.Delete(command.Resource);

        logger.WriteInfo("Deleted " + command.Resource);

 

        return new OperationResult.SeeOther

                   {

                       RedirectLocation = command.RedirectLocation

                   };

    }

}

This is how you would issue the delete command from your handler:

public class ResourceHandler

{

    readonly ISession session;

 

    public ResourceHandler(ISession session)

    {

        this.session = session;

    }

 

    public OperationResult Delete(int id)

    {

        var resource = session.Get<Resource>(id);

 

        return new DeleteCommand<Resource>(resource)

                   {

                       RedirectLocation = typeof(HomeResource).CreateUri()

                   };

    }

}

Last thing we need to do is register our pipeline contributor and command handler so they will be used.

ResourceSpace.Uses.PipelineContributor<CommandOperationResultInvokerContributor>();

ResourceSpace.Uses.CustomDependency<

    CommandOperationResultHandler<DeleteCommand<Resource>>,

    DeleteCommandHandler<Resource>>(DependencyLifetime.Transient);

That’s all there is to it.

Personally I prefer how this works in Open Rasta but then I would say that. I like how the result is manipulated by adding a step to the pipeline rather than having to override behaviour as in MVC. It just seems tidier and more flexible. The other differences in implementation could be transferred and are probably a matter of personal preference.

Which do you think is better?