我最近一直在研究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);
}一种可能的解决方案是返回a CommandResult<T>而不是a 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但这将是一个非常复杂的过程。
因此,总的来说,我很难转换这种“简单”的动作。
我在寻找输入。我在这里完全错了吗?