ASP.net MVC中的流畅验证 - 数据库验证

时间:2011-11-29 19:02:51

标签: asp.net-mvc fluentvalidation asp.net-mvc-validation

我在ASP.net MVC 3项目中使用了Fluent Validation框架。到目前为止,我的所有验证都非常简单(确保字符串不为空,只有一定长度等),但现在我需要验证数据库中是否存在某些内容。

  1. 在这种情况下应该使用流利验证吗?
  2. 如果应使用Fluent验证完成数据库验证,那么如何处理依赖项?验证器类是自动创建的,我需要以某种方式将其传递给我的一个存储库实例,以便查询我的数据库。
  3. 我正在尝试验证的一个例子可能是:

    我的页面上有一个下拉列表,其中包含所选项目的列表。我想在尝试保存新记录之前验证他们选择的项目是否实际存在于数据库中。

    修改
    以下是Fluent Validation框架中常规验证的代码示例:

    [Validator(typeof(CreateProductViewModelValidator))]
    public class CreateProductViewModel
    {
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
    
    public class CreateProductViewModelValidator : AbstractValidator<CreateProductViewModel>
    {
        public CreateProductViewModelValidator()
        {
            RuleFor(m => m.Name).NotEmpty();
        }
    }
    

    控制器:

    public ActionResult Create(CreateProductViewModel model)
    {
        if(!ModelState.IsValid)
        {
            return View(model);
        }
    
        var product = new Product { Name = model.Name, Price = model.Price };
        repository.AddProduct(product);
    
        return RedirectToAction("Index");
    }
    

    如您所见,我自己从不创建验证器。这是因为Global.asax中的以下行:

    FluentValidation.Mvc.FluentValidationModelValidatorProvider.Configure();
    

    问题是现在我有一个验证器需要使用存储库与我的数据库进行交互,但由于我没有创建验证器,我不知道如何传入依赖关系,除了硬编码具体类型。

5 个答案:

答案 0 :(得分:1)

link可帮助您实现所需内容,而无需手动实例化和手动验证模型。此链接直接来自FluentValidation论坛。

答案 1 :(得分:0)

我正在使用FluentValidation进行DataBase验证。只需将验证类传递给Ctor中的会话。并在动作中进行验证,如:

var validationResult = new ProdcutValidator(session).Validate(product);

更新:根据您的示例,我添加了我的示例...

public class CreateProductViewModel
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

public class CreateProductViewModelValidator : abstractValidator<CreateProductViewModel>
{
    private readonly ISession _session;
    public CreateProductViewModelValidator(ISession session)
    {
        _session = session;
        RuleFor(m => m.Name).NotEmpty();
        RuleFor(m => m.Code).Must(m, Code => _session<Product>.Get(Code) == null);

    }
}
Controller:

public ActionResult Create(CreateProductViewModel model)
{
    var validator = new CreateProductViewModelValidator();
    var validationResult =validator.Validate(model);

    if(!validationResult.IsValid)
    {
        // You will have to add the errors by hand to the ModelState's errors so the
        // user will be able to know why the post didn't succeeded(It's better writing 
        // a global function(in your "base controller" That Derived From Controller)
        // that migrate the validation result to the 
        // ModelState so you could use the ModelState Only.
        return View(model);
    }

    var product = new Product { Name = model.Name, Price = model.Price };
    repository.AddProduct(product);

    return RedirectToAction("Index");
}

第二次更新:
    如果你坚持使用无参数构造函数,你将不得不使用一些Inversion Of control container,一个类似于对象Factory的静态类。 像这样使用它:

public class CreateProductViewModelValidator : abstractValidator<CreateProductViewModel>
{
    private readonly ISession _session;
    public CreateProductViewModelValidator()
    {
        _session = IoC.Container.Reslove<ISession>();
        RuleFor(m => m.Name).NotEmpty();
        RuleFor(m => m.Code).Must(m, Code => _session<Product>.Get(Code) == null);

    }
}

你可以找到许多IoC容器,最着名的是WindsorNinject, 您需要注册 - 指示容器一次以解析所有ISession以返回您的会话对象。

答案 2 :(得分:0)

难道你不能只创建自己的验证方法,在哪里开始数据库验证?

    RuleFor(m => m.name)
           .Must(BeInDatabase)

    private static bool BeInDatabase(string name)
    {
        // Do database validation and return false if not valid
        return false;
    }

答案 3 :(得分:0)

这对你有用的另一种方法是使用Constructor注入。虽然这种方法不像使用IoC库那样明确,但如果您使用静态方式访问或获取会话,它可能会有所帮助。

public class CreateProductViewModelValidator
{
    private ISession _session;

    public CreateProductViewModelValidator()
        :this(SessionFactory.GetCurrentSession()) //Or some other way of fetching the repository.
    {

    }

    internal CreateProductViewModelValidator(ISession session)
    {
        this._session = session;
        RuleFor(m => m.Name);//More validation here using ISession...
    }
}

答案 4 :(得分:0)

我花了很多时间考虑这个完全相同的问题。我正在使用ninject将我的存储库注入我的Web UI层,以便我的Web UI只能通过接口访问数据库。

我希望能够验证访问数据库的内容,例如检查重复的名称,因此我的验证需要访问注入的存储库。我认为最好的方法是通过手动方法而不是MVC集成方式设置Fluent验证。例如:

创建验证类(可以传入存储库接口):

public class CategoryDataBaseValidation : AbstractValidator<CategoryViewModel>
{

    private IRepository repository;

    public CategoryDataBaseValidation (IRepository repoParam) 
    {

        repository = repoParam;

        RuleFor(Category => Category.Name).Must(NotHaveDuplicateName).WithMessage("Name already exists");
    }

    private bool NotHaveDuplicateName(string name) 
    {

       List<Category> c = repository.Categories.ToList(); //Just showing that you can access DB here and do what you like.
       return false;


    }
}

}

然后在你的控制器中你可以创建一个上面的类的实例并传入存储库(ninject会在控制器构造函数中注入)

 [HttpPost]
    public ActionResult Create(CategoryViewModel _CategoryViewModel )
    {

        CategoryDataBaseValidation validator = new CategoryDataBaseValidation (repository);

        ValidationResult results = validator.Validate(_CategoryViewModel );

       if (results.IsValid == false)
        {

            foreach (var failure in results.Errors)
            {

              //output error

            }

        }

        return View(category);
    }

上述两个文件都可以存在于Web UI项目中,您也可以使用标准的MVC DataAnnotations进行客户端验证。

只是想我会提出意见/帮助某人。