Nella nostra applicazione abbiamo CQRS: abbiamo IAsyncCommand
con IAsyncCommandHandler<IAsyncCommand>
.MVC5 Async ActionResult. È possibile?
Di solito il comando è trasformati tramite il mediatore in questo modo:
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);
che funziona bene. Ora ho notato che faccio un sacco di codice ripetitivo in azioni di controllo:
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());
}
E questo modelli ripete più e più in molti-molti regolatori. semplificazione Così per i comandi single-threaded che ho fatto:
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
}
E dopo qualche impianto idraulico fatto, la mia azione di controllo è simile al seguente:
public virtual ActionResult Create(DoStuffCommand command)
{
return ProcessCommand(command, View(command), RedirectToAction(MVC.HomePage.Index()));
}
Questo funziona bene per sync-comandi dove ho don' t Devo fare i modelli async-await
. Appena provo a eseguire le operazioni async
, questo non viene compilato, in quanto non esiste alcun AsyncActionResult
in MVC (o non esiste e non riesco a trovarlo) e non riesco a rendere il framework MVC utilizza le operazioni asincrone su void ExecuteResult(ControllerContext context)
.
Quindi, qualche idea su come posso realizzare un'implementazione generica dell'azione del controller che ho citato in cima alla domanda?
Non vedo dove viene chiamato qualcosa "AsyncActionResult". È sufficiente restituire un'attività o Task se si implementa un metodo generico. Le azioni asincrone restituiscono sempre le attività. 'async void' è una sintassi molto specifica usata solo per gestori di eventi asincroni (o metodi simili ai gestori) e * da nessuna parte * altro. L'equivalente asincrono di un metodo 'void' è una funzione che restituisce' Task'. L'equivalente di una funzione è una funzione che restituisce un 'Task ' –
@PanagiotisKanavos sì, sono abbastanza consapevole che non dovrei usare 'async void'. E non posso semplicemente restituire Task perché devo controllare se 'ModelState' è in uno stato valido prima di eseguire il mediatore, e ciò implica passare attraverso la pipeline MVC e tirare fuori' ModelState' dal framework in qualche modo. –
trailmax
Sembra che tu stia mescolando preoccupazioni diverse, come la richiesta in arrivo con l'azione stessa * e * il risultato previsto. A questo punto la tua classe ProcessCommandResult sembra un controller. Se si desidera sovrascrivere la convalida, l'associazione ecc., Esistono altri meccanismi in MVC. In effetti, ciò che hai qui viola CQRS - stai usando la risposta (ActionResult) come se fosse il comando stesso implementando ICommand. –