MVC5 Async ActionResult.那可能吗?

tra*_*max 6 c# asp.net-mvc asynchronous cqrs

在我们的应用中有CQRS:我们IAsyncCommand有IAsyncCommandHandler<IAsyncCommand>.

通常,命令通过Mediator处理,如下所示:

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); 
Run Code Online (Sandbox Code Playgroud)

这很好.现在我注意到我在控制器动作中做了很多重复的代码:

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());
}
Run Code Online (Sandbox Code Playgroud)

这种模式在许多控制器中反复重复.因此,对于单线程命令,我已经完成了简化:

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);
    }
    // plumbing code
}
Run Code Online (Sandbox Code Playgroud)

完成一些管道后,我的控制器操作如下所示:

public virtual ActionResult Create(DoStuffCommand command)
{
    return ProcessCommand(command, View(command), RedirectToAction(MVC.HomePage.Index()));
}
Run Code Online (Sandbox Code Playgroud)

这适用于我不需要执行async-await模式的sync-commands .一旦我尝试进行async操作,这就不能编译,因为AsyncActionResult在MVC中没有(或者有我无法找到它)并且我无法使MVC框架使用异步操作void ExecuteResult(ControllerContext context).

那么,任何想法我如何能够在问题的基础上引用控制器动作的通用实现?

Sam*_*mbo 0

看起来 Action 仍然是处理逻辑的最佳位置,而不是使用 ActionResult。

如果代码重复,为什么不使用带有受保护的辅助方法的基类......?

public class BaseCommandController : Controller
{
    protected IMediator Mediator { get { return DependencyResolver.Current.GetService(typeof (IMediator)) as IMediator; } }

    public async virtual Task<ActionResult> BaseDoStuff<TCommand>(TCommand command, Func<ActionResult> success, Func<ActionResult> failure)
    {
        if (!ModelState.IsValid)
        {
            return failure();
        }

        var result = await Mediator.ProcessCommand(command);

        if (!result.IsSuccess())
        {
            AddErrorsToModelState(result);
            return failure();
        }

        return success();
    }

    private void AddErrorsToModelState(IResponse result)
    {
    }

}
Run Code Online (Sandbox Code Playgroud)

然后你的控制器的动作被渲染为......

public class DefaultController : BaseCommandController
{
    protected async virtual Task<ActionResult> DoStuff(DoStuffAsyncCommand command)
    {
        return await BaseDoStuff(command, () => RedirectToAction("Index"), () => View(command));
    }
}
Run Code Online (Sandbox Code Playgroud)