This project is read-only.

async Command Handlers

Oct 15, 2014 at 10:44 PM
In the documentation for SimpleInjector you have a couple of topics describing asynchronous handlers. In both cases you are using decorators to run command async. Is there a particular reason for this approach over maybe this;
    public interface IHandleCommandAsync<in TCommand>
    {
        Task Handle(TCommand command);
    }
Oct 16, 2014 at 8:56 AM
I think you are talking about the following example from the Decorators With Func<T> decoratoee-factories section:
public class AsyncCommandHandlerDecorator<T> : ICommandHandler<T> {
    private readonly Func<ICommandHandler<T>> factory;
 
    public AsyncCommandHandlerDecorator(Func<ICommandHandler<T>> factory) {
        this.factory = factory;
    }
 
    public void Handle(T command) {
        // Execute on different thread.
        ThreadPool.QueueUserWorkItem(state => {
            try {
                // Create new handler in this thread.
                ICommandHandler<T> handler = this.factory.Invoke();
                handler.Handle(command);
            } catch (Exception ex) {
                // log the exception
            }
        });
    }
}
There is a really big difference between this example and a handler that returns Task. The examples given in the documentation simply show a way to run commands on a background thread (in parallel), while the calling code (possibly a web request) continues executing and might even finish long before the background operation finishes processing.

The model you are describing uses the new asynchronous programming model, which simply means that no thread is unnecessarily blocked in the case the code waits for I/O. But in the end, the calling code is ‘waiting’ for the command handler to finish. So there are a lot of differences between the two. For instance:
  • With async/await the code flows synchronously, while the code in the example from the documentation runs in parallel (code runs in parallel on a background thread).
  • The async/await model makes very efficient use of threads (which might be useful in cloud hosting scenarios or on the desktop) while in the given example the handler still runs on one single background thread, which will block that thread when waiting for I/O.
  • Using async/await, exceptions flow back to the caller, while using the parallel model the client has no clue when the operation fails (that’s why there’s a ‘log the exception’ line in the example).
  • Applying async/await on your code base is not a cross-cutting concern, since it will cuts through almost all the code in your complete code base. The example I show is a cross-cutting concern, since you can just silently apply it, without any code to change. Do note though that running code in the background in an hostile environment like ASP.NET is not the greatest thing to do. Better would be to queue those commands in a transactional queue instead.
So what are the consequences of doing async/await with Simple Injector? If you’re doing async/await, you should start using the Execution Context Scope Lifestyle unless you’re running Web API, in which case you can use the WebApiLifestyle (which uses the Execution Context Scope Lifestyle under the covers). If you’re running MVC, you can keep using the WebRequestLifestyle.

Do note that I personally think that the performance benefits that the new async/await programming model brings, do in most cases not outweigh the extra costs that are involved caused by lowered code readability/maintainability and increased complexity. Although the new async/await model in C# is a great improvement over how we had to write code in the past, asynchronous code is still much more complex to grasp and debug than synchronous code is. I rather spend a few more CPU cycles (and perhaps add an extra server) instead of wasting often much more expensive developer CPU cycles. The coming years we'll see that C# developers will get more and more used to this asynchronous programming model, so the costs might decrease. But I believe the costs will always be higher than working with a synchronous model. This might not be a popular opinion, since async/await is the new cool thing.

Also note that I/O intensive or slow operations should normally be queued and processed in the background any way (using an message bus or by publishing domain events).

But either way, do note that you can't have both a asynchronous and synchronous version of your interface. Asynchronous programming is not something you can 'sometimes do'. If cuts through the complete call stack and anytime you have a method that does not return a Task (but does do some I/O) the performance benefits of async/await are gone. So IMO it doesn't make sense to have the two models in a single application.

I hope this helps.
Marked as answer by dot_NET_Junkie on 11/3/2014 at 1:23 AM
Oct 16, 2014 at 2:53 PM
Thanks for your usual thorough response.

Because async/await is the new black in the C# world, I'm going to have to do some serious testing and metrics to show cost/benefits over using background processing using bus or event mechanisms. I can't agree more that having Tasks, await & async scattered throughout your application clutters the model. Of course describing the bus mechanism and having services that handle messages scattered everywhere has it's own complications, but in general is probably easier to troubleshoot. In any case I'll need ammo to make those arguments.

Thanks Steven!