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' Login
azione 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 model
e quindi gestire il CommandResult
filtro in un post azione. Come discusso qui .
Un'implementazione del CommandResult
potrebbe essere così
public interface ICommandResult
{
bool IsSuccess { get; }
bool IsFailure { get; }
object Result { get; set; }
}
Tuttavia, ciò non risolve realmente il nostro problema Login
nell'azione, poiché esistono più stati di errore. Potremmo aggiungere questi stati di errore extra a, ICommandResult
ma 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 Exception
strategia (come suggerito qui ) perché abbiamo bisogno del modello. Forse puoi ottenere il modello dal Request
ma sarebbe un processo molto complicato.
Quindi, tutto sommato, faccio fatica a convertire questa "semplice" azione.
Sto cercando input. Sono totalmente nel torto qui?