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).
那么,任何想法我如何能够在问题的基础上引用控制器动作的通用实现?
看起来 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)