В нашем приложении у нас есть CQRS: у нас есть IAsyncCommand
с IAsyncCommandHandler<IAsyncCommand>
.MVC5 Async ActionResult. Это возможно?
Обычно команда обрабатывается через Посредника, как это:
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);
Это работает отлично. Теперь я заметил, что я делаю много повторяющегося код в контроллере действий:
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());
}
И это модель повторяется снова и снова во многих-многих контроллерах. Таким образом, для однопоточных команд я сделал упрощение:
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
}
И после того, как некоторые сантехнические сделано, мое действие контроллер выглядит следующим образом:
public virtual ActionResult Create(DoStuffCommand command)
{
return ProcessCommand(command, View(command), RedirectToAction(MVC.HomePage.Index()));
}
Это хорошо работает для синхронизации команд, где я не» t необходимо сделать async-await
узоров. Как только я попытаюсь выполнить операции async
, это не скомпилируется, так как в MVC нет AsyncActionResult
(или есть, и я не могу его найти), и я не могу сделать структуру MVC для использования асинхронных операций на void ExecuteResult(ControllerContext context)
.
Итак, любые идеи, как я могу сделать общую реализацию действия контроллера, которую я цитировал в верхней части вопроса?
Я не вижу, где участвует что-то под названием «AsyncActionResult». Просто верните задачу или задачу , если вы реализуете общий метод. Асинхронные действия всегда возвращают задачи. 'async void' - очень специфический синтаксис, используемый только для асинхронных обработчиков событий (или обработчиков подобных методов) и * нигде * else. Асинхронный эквивалент метода 'void' - это функция, возвращающая' Task'. Эквивалентом функции является функция, возвращающая 'Task ' –
@PanagiotisKanavos да, я вполне понимаю, что я не должен использовать 'async void'. И я не могу просто вернуть Task, потому что мне нужно проверить, находится ли «ModelState» в допустимом состоянии до того, как я запускаю посредник, и это связано с тем, что вы проходите через конвейер MVC и каким-то образом вытаскиваете «ModelState» из структуры. –
trailmax
Кажется, что вы смешиваете различные проблемы, такие как входящий запрос с самим действием * и * ожидаемый результат. На данный момент ваш класс ProcessCommandResult выглядит как контроллер. Если вы хотите переопределить проверку, привязку и т. Д., В MVC есть другие механизмы. Фактически, то, что у вас здесь, нарушает CQRS - вы используете ответ (ActionResult), как если бы это была сама команда, реализуя ICommand. –