Я в последнее время изучал CQRS / MediatR. Но чем больше я тренируюсь, тем меньше мне это нравится. Возможно, я что-то неправильно понял / все.
Так что все начинается с того, что вы утверждаете, что сводите свой контроллер к этому
public async Task<ActionResult> Edit(Edit.Query query)
{
var model = await _mediator.SendAsync(query);
return View(model);
}
Что идеально сочетается с тонкой направляющей контроллера. Однако это оставляет некоторые довольно важные детали - обработку ошибок.
Давайте посмотрим на Login
действие по умолчанию из нового проекта 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);
}
Преобразование, которое представляет нам кучу реальных проблем. Помните, что цель состоит в том, чтобы уменьшить его до
public async Task<IActionResult> Login(Login.Command command, string returnUrl = null)
{
var model = await _mediator.SendAsync(command);
return View(model);
}
Одним из возможных решений этой проблемы является возврат CommandResult<T>
вместо а, model
а затем обработка CommandResult
в фильтре после действия. Как обсуждено здесь .
Одна реализация CommandResult
может быть такой
public interface ICommandResult
{
bool IsSuccess { get; }
bool IsFailure { get; }
object Result { get; set; }
}
Однако это не решает нашу проблему в Login
действии, потому что есть несколько состояний отказа. Мы могли бы добавить эти дополнительные состояния отказа кICommandResult
но это отличное начало для очень раздутого класса / интерфейса. Можно сказать, что это не соответствует Единой Ответственности (SRP).
Другая проблема заключается в returnUrl
. У нас есть этот return RedirectToLocal(returnUrl);
кусок кода. Каким-то образом нам нужно обрабатывать условные аргументы, основанные на состоянии успеха команды. Хотя я думаю, что это можно сделать (я не уверен, что ModelBinder может сопоставить аргументы FromBody и FromQuery ( returnUrl
это FromQuery) одной модели). Можно только задаться вопросом, какие сумасшедшие сценарии могут возникнуть в будущем.
Проверка модели также стала более сложной, наряду с возвратом сообщений об ошибках. Возьми это как пример
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
Мы прилагаем сообщение об ошибке вместе с моделью. Такого рода вещи нельзя сделать с помощью Exception
стратегии (как предлагается здесь ), потому что нам нужна модель. Возможно, вы можете получить модель изRequest
но это будет очень сложный процесс.
В общем, мне трудно преобразовать это «простое» действие.
Я ищу входные данные. Я тут совершенно не прав?