【问题标题】:Custom validator to reuse in fluent validation在流式验证中重用的自定义验证器
【发布时间】:2020-06-27 10:30:39
【问题描述】:

我想实现流畅的验证,而无需重复验证相同的属性。我正在寻找一种可以重复使用验证的方法。

我有如下三个类,Customer 和 NewClass 都是一样的,只是 NewClass 继承了 PageRequest。

public sealed class Customer {

public int Id{get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
public string MiddleName {get; set;}
public string Address {get; set;}

}

public class PageRequest
{
     public int CurrentPage {get; set;}
     public int PerPage {get; set;}
     public string SortBy {get; set;}
}

public class NewClass : PageRequest
    {
        public int Id{get; set;}
    public string FirstName {get; set;}
    public string LastName {get; set;}
    public string MiddleName {get; set;}
    public string Address {get; set;}
    }

Fluent 验证如下:

public abstract class GetPaginatedDataRequestValidator<TRequest, TModel> : AbstractValidator<TRequest>
        where TRequest : PageRequest
    {
        protected GetPaginatedDataRequestValidator()
        {
            var properties = typeof(TModel).GetProperties().Select(x => x.Name).ToList();
            RuleFor(x => x.CurrentPage).Required().GreaterThan(0);
            RuleFor(x => x.PerPage).Required().GreaterThan(0);
            RuleFor(x => x.SortBy)
               .Must(x => properties.Contains(x, StringComparer.OrdinalIgnoreCase))
               .When(x => !string.IsNullOrEmpty(x.SortBy))
               .WithMessage("{PropertyName} must be a known property name of a " + typeof(TModel).Name.Humanize());
        }
    }


public class NewClassValidator : GetPaginatedDataRequestValidator<
            NewClass, SomeDto>
{
    public NewClassValidator()
        {
            const string message = "At least one of either first name, last name, address and postcode are required.";
           
            RuleFor(x => x.FirstName)
                .Required()
                .When(x => string.IsNullOrEmpty(x.LastName) 
                           && string.IsNullOrEmpty(x.Address) && string.IsNullOrEmpty(x.PostCode))
                .WithMessage(message);

            RuleFor(x => x.LastName)
                .Required()
                .When(x => string.IsNullOrEmpty(x.FirstName) 
                           && string.IsNullOrEmpty(x.Address) && string.IsNullOrEmpty(x.PostCode))
                .WithMessage(message);

            RuleFor(x => x.Address)
                .Required()
                .When(x => string.IsNullOrEmpty(x.LastName) 
                           && string.IsNullOrEmpty(x.FirstName) && string.IsNullOrEmpty(x.PostCode))
                .WithMessage(message);

            RuleFor(x => x.PostCode)
                .Required()
                .When(x => string.IsNullOrEmpty(x.LastName) 
                           && string.IsNullOrEmpty(x.Address) && string.IsNullOrEmpty(x.FirstName))
                .WithMessage(message);
        }
}

public class CustomerValidator : AbstractValidator<Customer>
    {
        public CustomerValidator()
        {
            const string message = "At least one of either first name, last name, address and postcode are required.";
           
            RuleFor(x => x.FirstName)
                .Required()
                .When(x => string.IsNullOrEmpty(x.LastName) 
                           && string.IsNullOrEmpty(x.Address) && string.IsNullOrEmpty(x.PostCode))
                .WithMessage(message);

            RuleFor(x => x.LastName)
                .Required()
                .When(x => string.IsNullOrEmpty(x.FirstName) 
                           && string.IsNullOrEmpty(x.Address) && string.IsNullOrEmpty(x.PostCode))
                .WithMessage(message);

            RuleFor(x => x.Address)
                .Required()
                .When(x => string.IsNullOrEmpty(x.LastName) 
                           && string.IsNullOrEmpty(x.FirstName) && string.IsNullOrEmpty(x.PostCode))
                .WithMessage(message);

            RuleFor(x => x.PostCode)
                .Required()
                .When(x => string.IsNullOrEmpty(x.LastName) 
                           && string.IsNullOrEmpty(x.Address) && string.IsNullOrEmpty(x.FirstName))
                .WithMessage(message);
        }
    }

您可以在客户和新类验证器中看到相同的属性验证。无论如何我可以创建一个自定义验证器并在 Customer 和 NewClass 验证器中重用?由于属性重复,是否需要对类结构进行任何修改?

【问题讨论】:

    标签: c# fluentvalidation


    【解决方案1】:

    您可以通过接口(ICustomer 或类似接口)统一 Customer 和 NewClass 类,为接口编写验证器,然后在 Customer/NewClass 验证器中 include 该验证器。

    编辑:MVP LINQPad 示例

    void Main()
    {
        var customerValidator = new CustomerValidator();
        var customer1 = new Customer();
        var customerResult1 = customerValidator.Validate(customer1);
        Console.WriteLine(customerResult1.Errors.Select(x => x.ErrorMessage));
    
        var newClassValidator = new NewClassValidator();
        var newClass1 = new NewClass { CurrentPage = -1, PerPage = -1, SortBy = "Foo" };
        var newClassResult1 = newClassValidator.Validate(newClass1);
        Console.WriteLine(newClassResult1.Errors.Select(x => x.ErrorMessage));
    }
    
    public interface ICustomer
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string MiddleName { get; set; }
        public string Address { get; set; }
        public string PostCode { get; set; }
    }
    
    public sealed class Customer : ICustomer
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string MiddleName { get; set; }
        public string Address { get; set; }
        public string PostCode { get; set; }
    }
    
    public class PageRequest
    {
        public int CurrentPage { get; set; }
        public int PerPage { get; set; }
        public string SortBy { get; set; }
    }
    
    public class NewClass : PageRequest, ICustomer
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string MiddleName { get; set; }
        public string Address { get; set; }
        public string PostCode { get; set; }
    }
    
    public class SomeDto
    {
    
    }
    
    public class ICustomerValidator : AbstractValidator<ICustomer>
    {
        public ICustomerValidator()
        {
            const string message = "At least one of either first name, last name, address and postcode are required.";
    
            RuleFor(x => x.FirstName)
                .NotEmpty()
                .When(x => string.IsNullOrEmpty(x.LastName) && string.IsNullOrEmpty(x.Address) && string.IsNullOrEmpty(x.PostCode))
                .WithMessage(message);
    
            RuleFor(x => x.LastName)
                .NotEmpty()
                .When(x => string.IsNullOrEmpty(x.FirstName) && string.IsNullOrEmpty(x.Address) && string.IsNullOrEmpty(x.PostCode))
                .WithMessage(message);
    
            RuleFor(x => x.Address)
                .NotEmpty()
                .When(x => string.IsNullOrEmpty(x.LastName) && string.IsNullOrEmpty(x.FirstName) && string.IsNullOrEmpty(x.PostCode))
                .WithMessage(message);
    
            RuleFor(x => x.PostCode)
                .NotEmpty()
                .When(x => string.IsNullOrEmpty(x.LastName) && string.IsNullOrEmpty(x.Address) && string.IsNullOrEmpty(x.FirstName))
                .WithMessage(message);
        }
    }
    
    public class CustomerValidator : AbstractValidator<Customer>
    {
        public CustomerValidator()
        {
            Include(new ICustomerValidator());
        }
    }
    
    public abstract class GetPaginatedDataRequestValidator<TRequest, TModel> : AbstractValidator<TRequest> where TRequest : PageRequest
    {
        protected GetPaginatedDataRequestValidator()
        {
            var properties = typeof(TModel).GetProperties().Select(x => x.Name).ToList();
            RuleFor(x => x.CurrentPage).GreaterThan(0);
            RuleFor(x => x.PerPage).GreaterThan(0);
            RuleFor(x => x.SortBy)
               .Must(x => properties.Contains(x, StringComparer.OrdinalIgnoreCase))
               .When(x => !string.IsNullOrEmpty(x.SortBy))
               //.WithMessage("{PropertyName} must be a known property name of a " + typeof(TModel).Name.Humanize());
               .WithMessage("{PropertyName} must be a known property name of a " + typeof(TModel).Name);
        }
    }
    
    public class NewClassValidator : GetPaginatedDataRequestValidator<NewClass, SomeDto>
    {
        public NewClassValidator()
        {
            Include(new ICustomerValidator());
        }
    }
    

    结果:

    这是使用您的定义;我添加了PostCode 作为验证器引用它并更改了Required 验证器(如果属性不能为空(例如,int),则删除,或者对于字符串,替换为NotEmpty,因为Required 不是内置验证器)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-26
      • 1970-01-01
      • 1970-01-01
      • 2018-09-08
      • 2014-09-27
      • 2016-02-10
      • 2018-10-21
      相关资源
      最近更新 更多