Async Method Caller - Easy async Without await
tl;dr: AsyncMethodCaller is used to call methods asynchronously. Execution will continue with other methods after the asynchronous call completes. Use this if you cannot use async and await to easily make testable asynchronous calls. Very useful in ViewModels.
The Problem
C# has over its lifetime accumulated many asynchronous programming models:
Before async and await, background workers have long been a preferred method for making asynchronous calls and creating non-blocking UIs in MVVM ViewModels. This is a simple example:
public class ViewModelWithBackgroundWorker {
private readonly BackgroundWorker worker;
public ViewModelWithBackgroundWorker() {
worker = new BackgroundWorker();
worker.DoWork += DoSomething;
worker.RunWorkerCompleted += WorkCompleted;
}
public string Message { get; set; }
public string Result { get; set; }
public void ExecuteAsync() {
Message = "Loading...";
worker.RunWorkerAsync();
}
private void DoSomething(object sender, DoWorkEventArgs e) {
e.Result = "Result";
}
private void WorkCompleted(object sender, RunWorkerCompletedEventArgs e) {
if (e.Error != null) {
HandleError(e.Error);
} else {
Message = "Completed";
Result = (string)e.Result;
}
}
private void HandleError(Exception exception) {
Message = exception.Message;
}
}
Unfortunately, this practice has a couple of obvious deficiencies:
- Testing the correctness of the asynchronous execution is not straight forward and often involve inheriting from the View Model
- The code is verbose and the program flow can be hard to follow
- Multiple BackgroundWorkers might be needed if different asynchronous operations are to be supported
The Solution
The obvious solution is using async and await from C# 5. C# 5 does not support Windows XP however, making this solution unattainable for many organizations.
Therefore I created AsyncMethodCaller. AsyncMethodCaller is used to call methods asynchronously and continue with other methods after execution completes. If you cannot use async and await, it makes asynchronous calls easy to understand and test.
The example from before can now be rewritten, with unit tests!
public class ViewModelWithAsyncMethodCaller {
private readonly AsyncMethodCaller asyncMethodCaller;
public ViewModelWithAsyncMethodCaller(AsyncMethodCaller asyncMethodCaller) {
this.asyncMethodCaller = asyncMethodCaller ?? new AsyncMethodCaller();
}
public string Message { get; set; }
public string Result { get; set; }
public void ExecuteAsync() {
Message = "Loading...";
asyncMethodCaller.CallMethodAndContinue(DoSomething, WorkCompleted, HandleError);
}
private string DoSomething() {
return "Result";
}
private void WorkCompleted(string result) {
Message = "Completed";
Result = result;
}
private void HandleError(Exception exception) {
Message = exception.Message;
}
}
[TestFixture]
public class ViewModelTests {
[Test]
public void DoSomething() {
var asyncMethodCaller = new TestAsyncMethodCaller();
var viewModel = new ViewModelWithAsyncMethodCaller(asyncMethodCaller);
viewModel.ExecuteAsync();
Assert.AreEqual("Loading...", viewModel.Message);
asyncMethodCaller.StartServiceAndWait();
Assert.AreEqual("Result", viewModel.Result);
Assert.AreEqual("Completed", viewModel.Message);
}
}
AsyncMethodCaller thus gives you the following advantages:
- The asynchronous code is isolated from the ViewModel-logic
- The code can be tested without inheritance, using tests before, under and after the asynchronous call
- Program flow is easy to follow
- Only one AsyncMethodCaller is needed
The source code is available on GitHub or as a nuget-package.