Gần đây tôi đã xem xét CQRS / MediatR. Nhưng tôi càng đi sâu vào thì tôi càng ít thích nó. Có lẽ tôi đã hiểu nhầm một cái gì đó / mọi thứ.
Vì vậy, nó bắt đầu tuyệt vời bằng cách tuyên bố giảm bộ điều khiển của bạn để điều này
public async Task<ActionResult> Edit(Edit.Query query)
{
var model = await _mediator.SendAsync(query);
return View(model);
}
Mà phù hợp hoàn hảo với hướng dẫn bộ điều khiển mỏng. Tuy nhiên, nó để lại một số chi tiết khá quan trọng - xử lý lỗi.
Hãy xem Login
hành động mặc định từ một dự án MVC mới
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);
}
Chuyển đổi điều đó cho chúng ta một loạt các vấn đề trong thế giới thực. Hãy nhớ mục tiêu là giảm nó xuống
public async Task<IActionResult> Login(Login.Command command, string returnUrl = null)
{
var model = await _mediator.SendAsync(command);
return View(model);
}
Một giải pháp khả thi cho vấn đề này là trả về một CommandResult<T>
thay vì a model
và sau đó xử lý CommandResult
trong bộ lọc hành động bài. Như đã thảo luận ở đây .
Một thực hiện CommandResult
có thể là như thế này
public interface ICommandResult
{
bool IsSuccess { get; }
bool IsFailure { get; }
object Result { get; set; }
}
Tuy nhiên, điều đó không thực sự giải quyết vấn đề của chúng ta trong Login
hành động, bởi vì có nhiều trạng thái thất bại. Chúng ta có thể thêm các trạng thái lỗi bổ sung này vào ICommandResult
nhưng đó là một khởi đầu tuyệt vời cho một lớp / giao diện rất cồng kềnh. Người ta có thể nói rằng nó không tuân thủ Trách nhiệm đơn lẻ (SRP).
Một vấn đề khác là returnUrl
. Chúng tôi có return RedirectToLocal(returnUrl);
đoạn mã này. Bằng cách nào đó chúng ta cần xử lý các đối số có điều kiện dựa trên trạng thái thành công của lệnh. Trong khi tôi nghĩ điều đó có thể được thực hiện (Tôi không chắc liệu ModelBinder có thể ánh xạ các đối số FromBody và FromQuery ( returnUrl
là FromQuery) thành một mô hình không). Người ta chỉ có thể tự hỏi những loại kịch bản điên rồ nào có thể đi xuống đường.
Xác thực mô hình cũng trở nên phức tạp hơn cùng với việc trả về các thông báo lỗi. Lấy điều này làm ví dụ
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
Chúng tôi đính kèm một thông báo lỗi cùng với mô hình. Loại điều này không thể được thực hiện bằng cách sử dụng một Exception
chiến lược (như được đề xuất ở đây ) bởi vì chúng ta cần mô hình. Có lẽ bạn có thể lấy mô hình từ Request
nhưng nó sẽ là một quá trình rất liên quan.
Vì vậy, tất cả trong tất cả tôi đang có một thời gian khó khăn để chuyển đổi hành động "đơn giản" này.
Tôi đang tìm kiếm đầu vào. Tôi hoàn toàn sai ở đây?