2

我正在探索使用 FluentValidation,因为它似乎是一个优雅的 API,用于在模型绑定时验证我的 ViewModel。我正在寻找有关如何使用此库以及从我的业务(服务)层正确集中验证并将其提升到视图的意见,而无需使用 2 种不同的方法来添加模型状态错误。

我愿意使用完全不同的 API,但本质上是想解决这个分支验证策略。

[旁注:我尝试的一件事是将我的业务方法移动到我的 FluentValidation 的自定义 RsvpViewModelValidator 类中并使用 .Must 方法,但在那里隐藏该调用似乎是错误的,因为如果我需要实际使用我的 Customer 对象,我将拥有重新查询它,因为它超出了范围]

示例代码:

[HttpPost]
public ActionResult AcceptInvitation(RsvpViewModel model)
{
    //FluentValidation has happened on my RsvpViewModel already to check that 
    //RsvpCode is not null or whitespace
    if(ModelState.IsValid)
    {
        //now I want to see if that code matches a customer in my database.
        //returns null if not, Customer object if existing
        customer = _customerService.GetByRsvpCode(model.RsvpCode);
        if(customer == null)
        {
            //is there a better approach to this?  I don't like that I'm
            //splitting up the validation but struggling up to come up with a 
            //better way.
            ModelState.AddModelError("RsvpCode", 
                string.Format("No customer was found for rsvp code {0}", 
                              model.RsvpCode);

            return View(model);
        }

        return this.RedirectToAction(c => c.CustomerDetail());
    }

    //FluentValidation failed so should just display message about RsvpCode 
    //being required
    return View(model);
}

[HttpGet]
public ActionResult CustomerDetail()
{
     //do work.  implementation not important for this question.
}
4

2 回答 2

2

为了结束这个问题(并使其可以接受)并总结评论:

业务/流程逻辑和验证逻辑是两个实体。除非验证与数据库相关联(例如检查唯一条目),否则没有理由将验证分组到一个位置。有些人负责模型中的信息,确保信息没有任何无效,有些人负责处理验证值在系统中的使用方式。考虑一下属性获取器/设置器与具有这些属性的方法中使用的逻辑。

话虽如此,分离流程(检查、错误处理等——任何与 UI 无关的)都可以在服务层中完成,这也倾向于保持应用程序DRY。然后动作只负责调用和呈现,而不是执行实际的工作单元。(另外,如果您的应用程序中的各种操作使用类似的逻辑,则检查都在一个位置,而不是在操作之间放在一起。(我记得检查客户表中是否有一个条目吗?))

此外,通过将其分解为层,您可以保持关注点的模块化和可测试性。(接受 RSVP 不依赖于 UI 中的操作,但现在它是服务中的一个方法,可以由该 UI 或移动应用程序调用)。

至于冒泡错误,我通常有一个跨越每一层的基本异常,然后我可以根据目的对其进行扩展。您可以很容易地使用枚举、布尔值、out参数或简单的布尔值(Rsvp 要么被接受,要么不被接受)。这仅取决于用户纠正问题所需的响应有多有限,或者可能更改工作流程以使错误不是问题或用户需要纠正的东西。

于 2013-04-07T03:07:31.953 回答
0

您可以在流畅的验证中拥有整个验证逻辑:

public class RsvpViewValidator : AbstractValidator<RsvpViewModel>
{
    private readonly ICustomerService _customerService = new CustomerService();
    public RsvpViewValidator()
    {
        RuleFor(x => x.RsvpCode)
            .NotEmpty()
            .Must(BeAssociatedWithCustomer)
            .WithMessage("No customer was found for rsvp code {0}", x => x.RsvpCode)
    }

    private bool BeAssociatedWithCustomer(string rsvpCode)
    {
        var customer = _customerService.GetByRsvpCode(rsvpCode);
        return (customer == null) ? false : true;
    }
}
于 2013-04-07T03:01:56.337 回答