C# 可在fluent验证中重用的自定义验证器

C# 可在fluent验证中重用的自定义验证器,c#,fluentvalidation,C#,Fluentvalidation,我希望在不重复验证相同属性的情况下实现流畅的验证。正在寻找一种可以重用验证的方法 我有如下三个类,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

我希望在不重复验证相同属性的情况下实现流畅的验证。正在寻找一种可以重用验证的方法

我有如下三个类,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);
        }
    }
公共抽象类GetPaginatedDataRequestValidator:AbstractValidator
其中TRequest:PageRequest
{
受保护的GetPaginatedDataRequestValidator()
{
var properties=typeof(TModel).GetProperties().Select(x=>x.Name).ToList();
(x=>x.CurrentPage).Required()的规则大于(0);
RuleFor(x=>x.PerPage).Required().greaterth(0);
规则(x=>x.SortBy)
.Must(x=>properties.Contains(x,StringComparer.OrdinalIgnoreCase))
.When(x=>!string.IsNullOrEmpty(x.SortBy))
.WithMessage(“{PropertyName}必须是“+typeof(TModel).name.Humanize())的已知属性名);
}
}
公共类NewClassValidator:GetPaginatedDataRequestValidator<
新建类,有些要>
{
公共NewClassValidator()
{
const string message=“至少需要名字、姓氏、地址和邮政编码中的一个。”;
RuleFor(x=>x.FirstName)
.Required()
.When(x=>string.IsNullOrEmpty(x.LastName)
&&string.IsNullOrEmpty(x.Address)和&string.IsNullOrEmpty(x.PostCode))
.WithMessage(消息);
RuleFor(x=>x.LastName)
.Required()
.When(x=>string.IsNullOrEmpty(x.FirstName)
&&string.IsNullOrEmpty(x.Address)和&string.IsNullOrEmpty(x.PostCode))
.WithMessage(消息);
规则(x=>x.Address)
.Required()
.When(x=>string.IsNullOrEmpty(x.LastName)
&&string.IsNullOrEmpty(x.FirstName)和&string.IsNullOrEmpty(x.PostCode))
.WithMessage(消息);
规则(x=>x.邮政编码)
.Required()
.When(x=>string.IsNullOrEmpty(x.LastName)
&&string.IsNullOrEmpty(x.Address)和&string.IsNullOrEmpty(x.FirstName))
.WithMessage(消息);
}
}
公共类CustomerValidator:AbstractValidator
{
公共CustomerValidator()
{
const string message=“至少需要名字、姓氏、地址和邮政编码中的一个。”;
RuleFor(x=>x.FirstName)
.Required()
.When(x=>string.IsNullOrEmpty(x.LastName)
&&string.IsNullOrEmpty(x.Address)和&string.IsNullOrEmpty(x.PostCode))
.WithMessage(消息);
RuleFor(x=>x.LastName)
.Required()
.When(x=>string.IsNullOrEmpty(x.FirstName)
&&string.IsNullOrEmpty(x.Address)和&string.IsNullOrEmpty(x.PostCode))
.WithMessage(消息);
规则(x=>x.Address)
.Required()
.When(x=>string.IsNullOrEmpty(x.LastName)
&&string.IsNullOrEmpty(x.FirstName)和&string.IsNullOrEmpty(x.PostCode))
.WithMessage(消息);
规则(x=>x.邮政编码)
.Required()
.When(x=>string.IsNullOrEmpty(x.LastName)
&&string.IsNullOrEmpty(x.Address)和&string.IsNullOrEmpty(x.FirstName))
.WithMessage(消息);
}
}
您可以在customer和newclass验证器中看到相同的属性验证。 我是否可以创建自定义验证器并在Customer和NewClass验证器中重用? 重复属性时是否需要修改类结构?
非常感谢您的帮助

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

编辑: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());
    }
}
void Main()
{
var customerValidator=新customerValidator();
var customer1=新客户();
var customerResult1=customerValidator.Validate(customer1);
Console.WriteLine(customerResult1.Errors.Select(x=>x.ErrorMessage));
var newClassValidator=newnewclassvalidator();
var newClass1=newnewclass{CurrentPage=-1,PerPage=-1,SortBy=“Foo”};
var newClassResult1=newClassValidator.Validate(newClass1);
WriteLine(newClassResult1.Errors.Select(x=>x.ErrorMessage));
}
公共接口ICustomer
{
公共int Id{get;set;}
公共字符串名{get;set;}
公共字符串LastName{get;set;}
公共字符串MiddleName{get;set;}
公共字符串地址{get;set;}
公共字符串邮政编码{get;set;}
}
公共密封类客户:ICustomer
{
公共int Id{get;set;}
公共字符串名{get;set;}
公共字符串LastName{get;set;}
公共字符串MiddleName{get;set;}
公共字符串地址{get;set;}
公共字符串邮政编码{get;set;}
}
公共类页面请求
{
public int CurrentPage{get;set;}
公共整型每页{get;set;}
公共字符串排序方式{get;set;}
}
公共类NewClass:PageRequest,ICustomer
{
公共int Id{get;set;}
公共字符串名{get;set;}
公共字符串LastName{get;set;}
公共字符串MiddleName{get;set;}
公共字符串地址{get;set;}
公共字符串邮政编码{get;set;}
}
公共课
{
}
公共类ICCustomerValidator:AbstractValidator
{
公共ICustomerValidator()
{
const string message=“名字、姓氏和,