Task Pattern and Dependency Injection

Ayende has written several articles about the Command/Task pattern that he uses to organize his code and allow limited background processing in his blogging platform, RacoonBlog.

I've found his suggestions and the command/task pattern to be quite valuable in keeping my code well organized, but I don't like his testing method which involves base classes, manual property injection, and alternate execution delegates. I wanted an approach that easily allowed me to use dependency injection to increase flexibility and test-ability with a mocking framework.

A very simplified version of Ayende's task structure is outlined below. The SendThanksEmail clearly depends on an EmailGateway property that Ayende's BackgroundTask provides.

public class SendThanksEmail : BackgroundTask
{
    private readonly string to;

    public SendThanksEmail(string to)
    {
        this.to = to;
    }

    public void Execute()
    {
        var mailMessage = this.BuildMailMessage();

        // How do we inject EmailGateway?
        this.EmailGateway.Send(mailMessage);
    }
}

public abstract class BackgroundTask
{   
    // Set by task executor or instantiated in the constructor
    public IEmailGateway EmailGateway { get; set; }

    public abstract void Execute();
}

This task would be instantiated and used in a controller as follows:

public ActionResult MyAction(ViewModel viewModel)
{
    var sendThanksEmail = new SendThanksEmail(viewModel.To);
    this.BackgroundTaskExecutor.ExecuteLater(sendThanksEmail);
    return this.RedirectToAction("Thanks");
}

The call to BackgroundTaskExecutor.ExecuteLater queues the task to be executed at a later time, which is inconsequential to this discussion.

Given the above scenario, how can we inject an IEmailGateway into the task? The two traditional methods of injection, constructor and property injection, are both awkward to use here. Constructor injection is awkward because requiring the SendThanksEmail constructor to accept an IEmailGateway means that an IEmailGateway must be available to the controller that instantiates the task. But by requiring the controller have access to the IEmailGateway, we're losing the greatest benefits of using the tasks, separation of concerns and encapsulation.

The second traditional option is to use property injection and expose a public IEmailGateway property on SendThanksEmail. In this scenario, the BackgroundTaskExecutor would be responsible for injecting the property dependencies before calling Execute(). This is an improvement because the controller isn't responsible for dependencies it doesn't directly use, but property injection makes for awkward and unclear APIs.

A solution that is testable, with a clean API, is method parameter injection.

Here are the interfaces I'll use:

public interface IBackgroundTask
{
}

public interface IBackgroundTaskExecutor
{
    void ExecuteLater(IBackgroundTask backgroundTask);
    void ExecuteTasks();
}

public interface IEmailGateway
{
    void SendEmail(MailMessage mailMessage);
}

You've probably noticed that IBackgroundTask doesn't doesn't define an Execute method. This is because the Execute method's signature will be different for every task and is where we will inject the task's dependencies. In this case we'll rely on the convention of the method being called Execute.

Here's the new testable SendThanksEmail task:

public class SendThanksEmail : IBackgroundTask
{
    private readonly string to;

    public SendThanksEmail(string to)
    {
        this.to = to;
    }

    public void Execute(IEmailGateway emailGateway)
    {
        var mailMessage = this.BuildMailMessage();
        emailGateway.Send(mailMessage);
    }
}

The core of the solution is the UnityBackgroundTaskExecutor. I'm using Unity as my container, but this functionality is not specific to Unity.

public class UnityBackgroundTaskExecutor : IBackgroundTaskExecutor
{
    private readonly IUnityContainer container;
    private readonly List<IBackgroundTask> backgroundTasks;

    public UnityBackgroundTaskExecutor(IUnityContainer container)
    {
        this.container = container;
        this.backgroundTasks = new List<IBackgroundTask>();
    }

    public void ExecuteLater(IBackgroundTask backgroundTask)
    {
        this.backgroundTasks.Add(backgroundTask)
    }

    public void ExecuteTasks()
    {
        foreach (var backgroundTask in backgroundTasks)
        {
            // Use Reflection to get the Execute method and its parameters
            var executeMethodInfo = backgroundTask.GetType().GetMethod("Execute");
            var parameterInfos = executeMethodInfo.GetParameters();

            // Resolve the parameters from the IoC container and invoke the Execute method
            var parameters = parameterInfos.Select(parameterInfo => this.container.Resolve(parameterInfo.ParameterType)).ToArray();
            executeMethodInfo.Invoke(backgroundTask, parameters);
        }
    }
}

For each task, the UnityBackgroundTaskExecutor inspects the Execute method for its parameters, resolves them from the container, and then invokes the task's Execute method.

A small amount of reflection and a simple naming convention allow us to create tasks that can be easily tested with a mocking library.

Chaining Azure Web.Config Transforms When Deploying From Source Control

Visual Studio 2012 introduced chained config transforms for publish profiles. Chained transforms allow an environment-specific transformation to be applied during the deployment process in addition to the standard build transform. A typical transform chain is Web.config » Web.Debug.config » Web.Staging.config. When publishing from Visual Studio to Azure Web Sites you can take advantage of this feature to customize you configuration per environment. Unfortunately, when publishing from source control, chained transformations not supported by the Azure build and deployment process.

Fortunately, there are two things you can do to get around this limitation. The first is to up-vote this feature suggestion so the Azure team knows the feature is needed. The second is to follow the four three steps below to add the feature into the build process yourself.

Step 1: Add an environment-specific transform file

The first step is to add a new transformation file specific to your deployment environment. For these instructions, I'll assume you have one Azure Web Site for staging purposes, so add a new file named Web.Staging.config and configure the transforms you need. The naming convention of Web.Environment.config is important as you'll see in the next step.

Step 2: Add the TransformXml task to your project file

The second step is to add a single new task to your web site project file. To do this, open the project file in a text editor and scroll all the way to the bottom. You will see a commented out target named AfterBuild with a friendly note that you can uncomment the target and add tasks to it. Take this advice, uncomment the AfterBuild target and add the TransformXml task as shown:

<Target Name="AfterBuild">
    <TransformXml Condition=" '$(Environment)' != '' " Source="Web.config" Transform="Web.$(Environment).config" Destination="Web.config" />
</Target>

If you're unfamiliar with MSBuild, $(Environment) refers to a property named Environment that you're going to set in the next step. The Source and Destination attributes should be obvious, while the Condition attribute ensures the task will only run when the Environment property is set.

Step 3: Add an app setting in Azure for your environment

The third step is to sign into your Azure Web Site portal and add a new app setting called SCM_BUILD_ARGS with a value of /p:Environment=Staging. This is done under the Configure page of your web site. The value specifies the Environment property that will be passed to MSBuild. Ensure the value matches the environment name of your transform file.

Configure Environment App Setting

Step 4: Customize the site build script

Thanks to Amit Apple in the comments, this step is no longer needed.

The source control build system that powers Azure Web Sites calls a simple batch file to invoke the build process. Instead of using the default batch file, you'll supply your own and pass in the Environment property.

Follow these instructions for generating the build script files you'll need. When you're done, you'll have two new files in the root of your repository, .deployment and deploy.cmd. Open deploy.cmd in a text editor and find the line that calls MSBuild.

In the list of properties passed to the build command add Environment=%ENVIRONMENT%; and save the file. This will pass the ENVIRONMENT app setting set in step three to the build process as a property named Environment, which is referenced by the TransformXml task you added in step 2. The resulting line should resemble:

... /p:Environment=%ENVIRONMENT%;_PackageTempDir="%DEPLOYMENT_TEMP%"; ...

Deployment

Commit the new and updated files to your repository and you're done. The next time your project is built by the Azure Web Site build system your environment-specific Web.config transformations will be chained together.