BoroDrummer BoroDrummer - 28 days ago 18
C# Question

Generic Async Query Class

Based on this fantastic CQRS article, I have the following interfaces defined:

public interface IQuery<TResult>
{
}

public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
TResult Handle(TQuery query);
}


What would be the best way to implement a generic asynchronous QueryHandler? So far, I have a new interface definition to change the Handle() method to a void and to accept a new "Complete" event handler. Something like:

public interface IQueryHandlerAsync<TQuery, TResult> where TQuery : IQuery<TResult>
{
void Handle(TQuery query, RunWorkerCompletedEventHandler complete);
}


I could create new concrete classes for all of my query handlers implementing the IQueryHandlerAsync interface such as:

public class FindUsersBySearchTextQueryHandlerAsync
: IQueryHandlerAsync<FindUsersBySearchTextQuery, User[]>
{
private readonly IQueryHandler<FindUsersBySearchTextQuery, User[]> queryHandler;

public FindUsersBySearchTextQueryHandler(IQueryHandler<FindUsersBySearchTextQuery, User[]> queryHandler)
{
this.queryHandler = queryHandler;
}

public void Handle(FindUsersBySearchTextQuery query, RunWorkerCompletedEventHandler complete)
{
var worker = new BackgroundWorker();
worker.DoWork += DoWork;
worker.RunWorkerCompleted += complete;
worker.RunWorkerAsync(query);
}

void DoWork(object sender, DoWorkEventArgs e)
{
FindUsersBySearchTextQuery query = (FindUsersBySearchTextQuery)e.Argument;
queryHandler.Handle(query);
}
}


This Async concrete class would be very similar for all my queries. Is there a way to make this generic and maybe injected automatically?

Edit : Solution

Leave the interfaces alone, but change the Handle() method to return a Task:

public interface IQuery<TResult> { }

public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
Task<TResult> Handle(TQuery query);
}


In the concrete implementation wrap up the work of the Handle() method into a Task, start the task, and then return it.

public Task<User[]> Handle(FindUsersBySearchTextQuery query)
{
return TaskEx.Run(() =>
{
//Do slow operation
//...
return new User[] { ... };
});
}


Usage:

private async void GetUsers()
{
var users = await queryHandler.Handle(new FindUsersBySearchTextQuery
{
SearchText = searchText
});
}

Answer

You don't have to do anything. Just return Task<T>:

public class FindUsersBySearchTextQuery : IQuery<Task<User[]>>
{
}

public class FindUsersBySearchTextQueryHandler
    : IQueryHandler<FindUsersBySearchTextQuery, Task<User[]>>
{
    public async Task<User[]> Handle(FindUsersBySearchTextQuery query)
    {
        // ...
    }
}

Task<T> is part of .NET 4.0 so you can use that. You can use C# 5.0 and this allows you to use the new async/await keywords, which makes programming a lot easier. And if this is not available, you can still use the TPL API from .NET 4.0 to make this possible.

If, on the other hand you're sure that all your queries are always asynchronous, you can change the interface to the following:

public interface IQuery<TResult> { }

public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
    // Note the Task<T> here
    Task<TResult> Handle(TQuery query);
}

This approach is generally better, because now you move the knowledge about whether things are asynchronous or not out of the query definition. The query definition (e.g. class FindUsersBySearchTextQuery : IQuery<User[]>) should just be concerned about which data is returned, whether or not sonmething is async is an implementation details (unfortunately it's an implementation detail that infects your call stack like a virus, but still an implementation detail).

Comments