Mats Lycken development

.NET and web related blog posts

NAVIGATION - SEARCH

A cleaner solution to thread handling in Lightswitch

Too many Dispatcher.BeginInvoke!

One Issue that I've had since I started creating Lightswitch apps with Silverlight was all the Dispatcher.BeginInvoke calls that was needed to do different tasks in code.
I've always felt that the code felt really cluttered and hard to read when you had nested calls to dispatchers to handle things like showing a messagebox after updating some data.
The thing is that all code that affect UI must happen on the UI thread while all code that has anything to do with data must happen on the a background thread.
When you want to update some property on your screen or call a query you need to do something like
this.Details.Dispatcher.BeginInvoke(() =>
    {
        this.Customers.Refresh();
    });

If you then wanted to show a message box that said that the operation had completed, or maybe display an error message, you have to switch back to the UI thread.
this.Details.Dispatcher.BeginInvoke(() =>
    {
        this.Customers.Refresh();
        // Dispatchers.Main is the UI thread
        Dispatchers.Main.BeginInvoke(() =>
            {
                this.ShowMessageBox("Refreshed data!");
            });
    });
What I really wanted to have is a way to set up a list of actions that would execute on after the other and they would themselves handle being invoked on the correct thread.

My solution - the TaskRunner

I decided to create a class TaskRunner, that takes a list of tasks and then makes sure that they will execute in that order. One after the other.

With the TaskRunner and some Task-classes I could go from the code above to the following
var refreshTask = new BackgroundTask(() => Customers.Refresh());
var notifyTask = new ForegroundTask(() => this.ShowMessageBox("Refreshed data!"));
var taskRunner = new TaskRunner(Dispatchers.Main, this.Details.Dispatcher)
                        .SetTasks(refreshTask, notifyTask)
                        .Execute();
This example is not that complex so the gains aren't that great but when you have more tasks and also throw error handling and conditionals into the mix you can easily end up with a big ball of mud that's hard to debug. I think that the task runner makes it easier to see how the tasks execute after each other when you set them up in a list like this than when you have the nested Dispatcher calls above.

The simple tasks

All tasks inherit from an abstract class called TaskBase
public abstract class TaskBase
{
    public Exception Exception { get; protected set; }

    public string ErrorMessage { get; set; }

    public bool ContinueOnError { get; set; }

    public abstract void Run(IDispatcher foregroundDispatcher, IDispatcher backgroundDispatcher, Action nextAction = null);

    public event EventHandler ErrorOccured;

    protected void OnErrorOccured(EventArgs e)
    {
        if (ErrorOccured != null)
        {
            ErrorOccured(this, e);
        }
    }
}
The base class contains some common features like an Exception that can be set if an error occurs during the execution of the task and also a property that states if the chain of tasks should continue to execute if the task fails.
The big thing in the class is the Run method that takes two Dispatchers and an action as parameters. The two different dispatchers are the foreground and background dispatchers and the task itself decides on which it should execute. The nextAction parameter is an action that is created by the TaskRunner and will be next task in the chain, or null if this is the last task.

I could then create a SimpleTask class that will handle the basic tasks that run either on a foreground thread on on the background.
public abstract class SimpleTask : TaskBase
{
    protected Action action;

    public SimpleTask(Action action)
    {
        this.action = action;
    }

    protected void RunOnDispatcher(IDispatcher dispatcher, Action nextAction)
    {
        dispatcher.BeginInvoke(() =>
        {
            try
            {
                action();
            }
            catch (Exception exc)
            {
                this.Exception = exc;
                OnErrorOccured(EventArgs.Empty);
                if (ContinueOnError == false)
                {
                    return;
                }
            }
            if (nextAction != null)
            {
                nextAction();
            }
        });
    }
}

The foreground and background tasks then became really easy to write.

public class ForegroundTask : SimpleTask
{
    public ForegroundTask(Action action)
        : base(action)
    { }

    public override void Run(IDispatcher foregroundDispatcher, IDispatcher backgroundDispatcher, Action nextAction = null)
    {
        RunOnDispatcher(foregroundDispatcher, nextAction);
    }
}

public class BackgroundTask : SimpleTask
{
    public BackgroundTask(Action action)
        : base(action)
    { }

    public override void Run(IDispatcher foregroundDispatcher, IDispatcher backgroundDispatcher, Action nextAction = null)
    {
        RunOnDispatcher(backgroundDispatcher, nextAction);
    }
}

A little more complex task - RiaTask

The main reason I created this solution is that when you do calls to RIA services you usually end up with a big mess. The thing is that when you create an instance of a context you must do it on the UI thread, but you usually always want to handle the result on the background thread.

In this example I have created a service that returns a string describing a customers age (kind of weird, I know). If an error occurs I want to notify the user with a message box. If the operation succeeds I want to update the selected customer, save the screen and notify the user with a message box.

Here's the code
Dispatchers.Main.BeginInvoke(() =>
    {
        var context = new RiaContext();
        var operation = context.GetPersonAgeStatus(Customers.SelectedItem.Age);
        operation.Completed += (s, e) =>
        {
            if (operation.HasError == true)
            {
                this.ShowMessageBox("Error occured during RIA task!");
            }
            else
            {
                this.Details.Dispatcher.BeginInvoke(() =>
                    {
                        Customers.SelectedItem.AgeStatus = operation.Value as string;
                        this.Save();
                        Dispatchers.Main.BeginInvoke(() => 
                            {
                                this.ShowMessageBox("We did it!");
                            });
                    });
            }
        };
    });
If there is a better and cleaner way to do this I would greatly appreciate any pointers!

I decided to create a Task that would hide away all that and just let me focus on what I want to do. When I set up my task I just want to specify what type of context I want, what I want to do with the result and set any error message that should be displayed.

Here is the RiaTask
var riaTask = new RiaTask<RiaContext>
{
    Execute = (ctx) => ctx.GetPersonAgeStatus(Customers.SelectedItem.Age),
    ErrorMessage = "Error occured during RIA task!",
    OnSuccessBackground = (op) => 
    {
        Customers.SelectedItem.AgeStatus = op.Value as string;
    }
};

The RiaContext is the type of my RIA service context class. The task will new-up an instance of the context by itself on the foreground thread and execute it there. I can then set an action that will run on the background thread to handle the return value. If an exception occurs the supplied error message as well as the exception itself will be passed to the TaskRunner that executes the task.

Using this I could rewrite the first RIA example like this using tasks
var riaTask = new RiaTask
{
    Execute = (ctx) => ctx.GetPersonAgeStatus(Customers.SelectedItem.Age),
    ErrorMessage = "Error occured during RIA task!",
    OnSuccessBackground = (op) => 
    {
        Customers.SelectedItem.AgeStatus = op.Value as string;
    }
};
var updateDataTask = new BackgroundTask(() => this.Save());
var notifyUserTask = new ForegroundTask(() => this.ShowMessageBox("We did it!"));

var taskRunner = new TaskRunner(Dispatchers.Main, this.Details.Dispatcher)
{
    OnError = (exception, errorMessage) => this.ShowMessageBox("An error occured: " + errorMessage + "\n" + exception.Message)
};
taskRunner.SetTasks(riaTask, updateDataTask, notifyUserTask);
taskRunner.Execute();
The code for RiaTask is a bit longer but you can check it out in the attached source code.

Innards of the TaskRunner

Without further ado here is the TaskRunner class
public class TaskRunner
{
    private TaskBase[] tasks;
    private IDispatcher foregroundDispatcher;
    private IDispatcher backgroundDispatcher;

    public Action OnError { get; set; }

    public TaskRunner(IDispatcher foregroundDispatcher, IDispatcher backgroundDispatcher)
    {
        this.foregroundDispatcher = foregroundDispatcher;
        this.backgroundDispatcher = backgroundDispatcher;
    }
        
    public TaskRunner SetTasks(params TaskBase[] tasks)
    {
        this.tasks = tasks;
        return this;
    }

    public void Execute()
    {
        var tasksInChain = SetupChain();
        tasksInChain();
    }

    private Action SetupChain(int taskIndex = 0)
    {
        if (taskIndex >= tasks.Length)
        {
            return null;
        }

        tasks[taskIndex].ErrorOccured += TaskRunner_ErrorOccured;
        Action nextAction = SetupChain(taskIndex + 1);                
        var runTaskAction = new Action(() =>
        {
            tasks[taskIndex].Run(foregroundDispatcher, backgroundDispatcher, nextAction);
        });
        return runTaskAction;
    }

    void TaskRunner_ErrorOccured(object sender, EventArgs e)
    {
        var sourceTask = sender as TaskBase;
        OnError(sourceTask.Exception, sourceTask.ErrorMessage ?? string.Empty);
    }
}

The main grunt of the class is in the SetupChain method that recursively traverses each task and creates a chain of actions. The links in the chain are the nextAction parameters that are passed to each task in the Run method. This way each task can make sure that the next task is executed after this task, regardless of which thread it will execute on.

Future improvements

I just created this during the weekend so it still has some improvements to be made. 
One thing I'm thinking of is checking which thread we are on in the simple tasks so that we don't have to do Dispatcher.BeginInvoke if we are going to execute the action on the thread we currently are on.
Another new Task I'm thinking about implementing is a ParallellTask (maybe not the best name), but it would take a list of tasks and execute them all at once and would only call the nextAction after all the tasks had executed.
That way you could easily do multiple RIA service calls and only after all them had completed would you call the next task in the chain.

If any of you have any suggestions for improvements or other comments they are all greatly appreciated. I've attached the source code to this post and would love if you would download it and play around with it.

Take care!
/Mats

TaskRunnerDemo.zip (2,9MB)