In our application, we have CQRS: we have IAsyncCommand
with IAsyncCommandHandler<IAsyncCommand>
.
Typically, a command is processed through an intermediary as follows:
var mediator = //get mediator injected into MVC controller via constructor var asyncCommand = // construct AsyncCommand // mediator runs ICommandValidator and that returns a list of errors if any var errors = await mediator.ProcessCommand(asyncCommand);
It works great. Now I noticed that I am doing a lot of repeating code in controller actions:
public async virtual Task<ActionResult> DoStuff(DoStuffAsyncCommand command) { if (!ModelState.IsValid) { return View(command); } var result = await mediator.ProcessCommandAsync(command); if (!result.IsSuccess()) { AddErrorsToModelState(result); return View(command); } return RedirectToAction(MVC.HomePage.Index()); }
And these patterns are repeated over and over in many controllers. Therefore, for single-threaded commands I made a simplification:
public class ProcessCommandResult<T> : ActionResult where T : ICommand { private readonly T command; private readonly ActionResult failure; private readonly ActionResult success; private readonly IMediator mediator; public ProcessCommandResult(T command, ActionResult failure, ActionResult success) { this.command = command; this.success = success; this.failure = failure; mediator = DependencyResolver.Current.GetService<IMediator>(); } public override void ExecuteResult(ControllerContext context) { if (!context.Controller.ViewData.ModelState.IsValid) { failure.ExecuteResult(context); return; } var handlingResult = mediator.ProcessCommand(command); if (handlingResult.ConainsErrors()) { AddErrorsToModelState(handlingResult); failure.ExecuteResult(context); } success.ExecuteResult(context); }
And after performing any plumbing, the action of my controller is as follows:
public virtual ActionResult Create(DoStuffCommand command) { return ProcessCommand(command, View(command), RedirectToAction(MVC.HomePage.Index())); }
This works well for synchronization commands, where I don't need to do async-await
templates. As soon as I try to perform async
operations, this will not compile, since MVC does not have AsyncActionResult
(or is, and I cannot find it), and I cannot force the MVC structure to use async operations for void ExecuteResult(ControllerContext context)
.
So, any ideas how I can make a general implementation of the controller action that I quoted at the top of the question?