Ultimamente ho esaminato CQRS / MediatR. Ma più approfondisco, meno mi piace. Forse ho frainteso qualcosa / tutto.
Quindi è fantastico affermando di ridurre il controller a questo
public async Task<ActionResult> Edit(Edit.Query query)
{
var model = await _mediator.SendAsync(query);
return View(model);
}
Che si adatta perfettamente alla linea guida del controller sottile. Tuttavia lascia fuori alcuni dettagli piuttosto importanti: la gestione degli errori.
Vediamo l' Loginazione predefinita di un nuovo progetto MVC
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
_logger.LogInformation(1, "User logged in.");
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
}
if (result.IsLockedOut)
{
_logger.LogWarning(2, "User account locked out.");
return View("Lockout");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
La conversione ci presenta un sacco di problemi nel mondo reale. Ricorda che l'obiettivo è ridurlo a
public async Task<IActionResult> Login(Login.Command command, string returnUrl = null)
{
var model = await _mediator.SendAsync(command);
return View(model);
}
Una possibile soluzione a questo è quella di restituire un CommandResult<T>anziché un modele quindi gestire il CommandResultfiltro in un post azione. Come discusso qui .
Un'implementazione del CommandResultpotrebbe essere così
public interface ICommandResult
{
bool IsSuccess { get; }
bool IsFailure { get; }
object Result { get; set; }
}
Tuttavia, ciò non risolve realmente il nostro problema Loginnell'azione, poiché esistono più stati di errore. Potremmo aggiungere questi stati di errore extra a, ICommandResultma questo è un ottimo inizio per una classe / interfaccia molto gonfia. Si potrebbe dire che non è conforme alla responsabilità singola (SRP).
Un altro problema è il returnUrl. Abbiamo questo return RedirectToLocal(returnUrl);pezzo di codice. In qualche modo dobbiamo gestire gli argomenti condizionali basati sullo stato di successo del comando. Mentre penso che potrebbe essere fatto (non sono sicuro se ModelBinder può mappare gli argomenti FromBody e FromQuery ( returnUrlè FromQuery) su un singolo modello). Ci si può solo chiedere che tipo di scenari folli potrebbero venire giù per la strada.
Anche la convalida del modello è diventata più complessa insieme alla restituzione di messaggi di errore. Prendi questo come esempio
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
Alleghiamo un messaggio di errore insieme al modello. Questo genere di cose non può essere fatto usando una Exceptionstrategia (come suggerito qui ) perché abbiamo bisogno del modello. Forse puoi ottenere il modello dal Requestma sarebbe un processo molto complicato.
Quindi, tutto sommato, faccio fatica a convertire questa "semplice" azione.
Sto cercando input. Sono totalmente nel torto qui?