繁体   English   中英

使用 FluentValidation 在 Blazor 中进行自定义验证

[英]Custom Validation in Blazor with FluentValidation

我正在尝试创建自定义复杂类型验证。 我创建了一个类型,它具有三个子类,每个子类都有一个自定义验证器。 验证器只是使用默认值。 一个具体的例子是,金额应该允许负数,但不管我如何尝试,它只允许 >0。 任何帮助表示赞赏。

剃刀页

<TSValidator />

TSValidator.sc

public class TSValidator : ComponentBase
{
    [CascadingParameter] private EditContext CurrentEditContext { get; set; }

    protected override void OnInitialized()
    {
        if (CurrentEditContext == null)
        {
            throw new InvalidOperationException($"{nameof(TSValidator)} requires a cascading parameter of type {nameof(EditContext)}. For example, you can use {nameof(TSValidator)} inside an {nameof(EditForm)}");
        }

        CurrentEditContext.AddFluentValidation();
    }
}

延期

    public static EditContext AddFluentValidation(this EditContext editContext)
    {
        if (editContext == null)
        {
            throw new ArgumentNullException(nameof(editContext));
        }

        var messages = new ValidationMessageStore(editContext);

        editContext.OnValidationRequested += (sender, eventArgs) => ValidateModel((EditContext) sender, messages);

        editContext.OnFieldChanged += (sender, eventArgs) =>
            ValidateField(editContext, messages, eventArgs.FieldIdentifier);

        return editContext;
    }

    public static void ValidateModel(EditContext editContext, ValidationMessageStore store)
    {
        var validator = GetValidatorForModel(editContext.Model);
        var validationResults = validator.Validate(editContext.Model);

        store.Clear();
        foreach (var error in validationResults.Errors)
        {
            store.Add(editContext.Field(error.PropertyName), error.ErrorMessage);
        }

        editContext.NotifyValidationStateChanged();
    }

    public static void ValidateField(EditContext editContext, ValidationMessageStore store,
        in FieldIdentifier fieldIdentifier)
    {
        var properties = new[] {fieldIdentifier.FieldName};
        var context = new FluentValidation.ValidationContext(fieldIdentifier.Model, new FluentValidation.Internal.PropertyChain(), new MemberNameValidatorSelector(properties));
        var validator = GetValidatorForModel(fieldIdentifier.Model);
        var validationResults = validator.Validate(context);

        store.Clear();
        store.Add(fieldIdentifier, validationResults.Errors.Select(error => error.ErrorMessage));

        editContext.NotifyValidationStateChanged();
    }

    public static IValidator GetValidatorForModel(object model)
    {
        var abstractValidatorType = typeof(AbstractValidator<>).MakeGenericType(model.GetType());
        var modelValidatorType = Assembly.GetExecutingAssembly().GetTypes()
            .FirstOrDefault(t => t.IsSubclassOf(abstractValidatorType));
        var modelValidatorInstance = (IValidator) Activator.CreateInstance(modelValidatorType);

        return modelValidatorInstance;
    }

家长班

public class SubmissionActivity
{
    public Submission Submission { get; set; } = new Submission();
    public List<SalesActivitySubmission> SalesActivitySubmissions { get; set; } = new List<SalesActivitySubmission>();
    public RepActivitySubmission RepActivitySubmission { get; set; } = new RepActivitySubmission();

}

父验证器

public class SubmissionActivityValidator : AbstractValidator<SubmissionActivity>
{
    public SubmissionActivityValidator()
    {
        RuleFor(s => s.RepActivitySubmission).SetValidator(new RepActivitySubmissionValidator());
        RuleForEach(s => s.SalesActivitySubmissions).SetValidator(new SalesActivitySubmissionValidator());
        RuleFor(s => s.Submission).SetValidator(new SubmissionValidator());
    }
}

儿童班

public class Submission
{
    [Key]
    public long SubmissionId { get; set; }
    public DateTime SubmissionDate { get; set; }
    public bool IsActive { get; set; }
    public int PropertyId { get; set; }
    public Property Property { get; set; }
    public DateTime CreatedOn { get; set; }
    public long CreatedBy { get; set; }
    public DateTime ModifiedOn { get; set; }
    public long ModifiedBy { get; set; }
}

public class SalesActivitySubmission
{
    [Key]
    public long SalesActivitySubmissionId { get; set; }
    public long? SubmissionId { get; set; }
    public Submission Submission { get; set; }
    public long? SellerId { get; set; }
    public User Seller { get; set; }
    public int? TicketHierarchyId { get; set; }
    public TicketHierarchy TicketHierarchy { get; set; }
    [Column(TypeName = "money")]
    [Range(-999999999999.99, 999999999999.99, ErrorMessage = "Please enter a positive amount of 999,999,999,999.99 or less.")]
    public decimal? Amount { get; set; }
    [Range(int.MinValue, int.MaxValue, ErrorMessage = "Please enter a valid positive number.")]
    public int? TicketSaleCount { get; set; }
    public DateTime CreatedOn { get; set; }
    public long CreatedBy { get; set; }
    public DateTime ModifiedOn { get; set; }
    public long ModifiedBy { get; set; }
}

public class RepActivitySubmission
{
    [Key]
    public int RepActivitySubmissionId { get; set; }
    public long SellerId { get; set; }
    public User Seller { get; set; }
    public long? SubmissionId { get; set; }
    public Submission Submission { get; set; }

    [Range(int.MinValue, int.MaxValue, ErrorMessage = "Please enter a valid positive number.")]
    public int? CompletedCalls { get; set; }

    [Range(int.MinValue, int.MaxValue, ErrorMessage = "Please enter a valid positive number.")]
    public int? Contacted { get; set; }

    [Range(int.MinValue, int.MaxValue, ErrorMessage = "Please enter a valid positive number.")]
    public int? Pitches { get; set; }

    [Range(int.MinValue, int.MaxValue, ErrorMessage = "Please enter a valid positive number.")]
    public int? Sales { get; set; }

    [Range(int.MinValue, int.MaxValue, ErrorMessage = "Please enter a valid positive number.")]
    public int? Referrals { get; set; }

    [Range(int.MinValue, int.MaxValue, ErrorMessage = "Please enter a valid positive number.")]
    public int? Appointments { get; set; }

    [Range(0, int.MaxValue, ErrorMessage = "Please enter a valid positive number.")]
    public int? HoursWorked { get; set; }

    [Range(0, int.MaxValue, ErrorMessage = "Please enter a valid positive number.")]
    public int? OvertimeHours { get; set; }

    [Range(0, int.MaxValue, ErrorMessage = "Please enter a valid positive number.")]
    public int? PTOHours { get; set; }

    [Column(TypeName = "money")]
    [Range(-999999999999.99, 999999999999.99, ErrorMessage = "Please enter a positive amount of 999,999,999.99 or less.")]
    public decimal? PropExpense { get; set; }
    public DateTime CreatedOn { get; set; }
    public long CreatedBy { get; set; }
    public DateTime ModifiedOn { get; set; }
    public long ModifiedBy { get; set; }
}

子验证器

public class SubmissionValidator : AbstractValidator<Submission>
{
    public SubmissionValidator()
    {
        RuleFor(s => s.SubmissionDate).NotEmpty().NotNull();
    }
}

public class SalesActivitySubmissionValidator : AbstractValidator<SalesActivitySubmission>
{
    public SalesActivitySubmissionValidator()
    {
        RuleFor(s => s.SalesActivitySubmissionId).NotNull().WithMessage("SalesActivitySubmissionId cannot be null");
        RuleFor(s => s.SubmissionId).NotNull().WithMessage("SubmissionId cannot be null.");
        RuleFor(s => s.SellerId).NotNull().WithMessage("SellerId cannot be null");
        RuleFor(s => s.TicketHierarchyId).NotNull().WithMessage("TicketHierarchyId cannot be null.");
        RuleFor(s => s.Amount).InclusiveBetween(0, 999999999999)
            .WithMessage("Amount must be within 0 and 999999999999, inclusively.");
        RuleFor(s => s.TicketSaleCount).GreaterThan(-1).LessThan(int.MaxValue)
            .WithMessage($"TicketSaleCount must be null or between 0 and {int.MaxValue} inclusively.");

    }
}

public class RepActivitySubmissionValidator : AbstractValidator<RepActivitySubmission>
{
    public RepActivitySubmissionValidator()
    {
        RuleFor(r => r.SellerId).NotNull().WithMessage("SellerId cannot be null.");
        RuleFor(r => r.SubmissionId).NotNull().WithMessage("SubmissionId cannot be null.");
        RuleFor(r => r.CompletedCalls).GreaterThanOrEqualTo(0).LessThanOrEqualTo(int.MaxValue)
            .WithMessage($"CompletedCalls must be between 0 and {int.MaxValue} inclusively");
        RuleFor(r => r.Contacted).GreaterThanOrEqualTo(0).LessThanOrEqualTo(int.MaxValue)
            .WithMessage($"Contacted must be between 0 and {int.MaxValue} inclusively");
        RuleFor(r => r.Pitches).GreaterThanOrEqualTo(0).LessThanOrEqualTo(int.MaxValue)
            .WithMessage($"Pitches must be between 0 and {int.MaxValue} inclusively");
        RuleFor(r => r.Sales).GreaterThanOrEqualTo(0).LessThanOrEqualTo(int.MaxValue)
            .WithMessage($"Sales must be between 0 and {int.MaxValue} inclusively");
        RuleFor(r => r.Referrals).GreaterThanOrEqualTo(0).LessThanOrEqualTo(int.MaxValue)
            .WithMessage($"Referrals must be between 0 and {int.MaxValue} inclusively");
        RuleFor(r => r.Appointments).GreaterThanOrEqualTo(0).LessThanOrEqualTo(int.MaxValue)
            .WithMessage($"Appointed must be between 0 and {int.MaxValue} inclusively");
        RuleFor(r => r.HoursWorked).GreaterThanOrEqualTo(0).LessThanOrEqualTo(int.MaxValue)
            .WithMessage($"HoursWorked must be between 0 and {int.MaxValue} inclusively");
        RuleFor(r => r.OvertimeHours).GreaterThanOrEqualTo(0).LessThanOrEqualTo(int.MaxValue)
            .WithMessage($"OvertimeHours must be between 0 and {int.MaxValue} inclusively");
        RuleFor(r => r.PTOHours).GreaterThanOrEqualTo(0).LessThanOrEqualTo(int.MaxValue)
            .WithMessage($"PTOHours must be between 0 and {int.MaxValue} inclusively");
        RuleFor(r => r.PropExpense).GreaterThanOrEqualTo(0).LessThan(1000000000000)
            .WithMessage("PropExpense must be between 0 and 999999999999.99.");
    }
}

它与允许负数的范围验证数据属性和不允许负数的流畅验证规则有什么关系吗? 似乎您使用不同的验证提供程序为相同的属性指定了两次验证规则,并且每个提供程序指定的规则都不同。

例如,

一个具体的例子是,金额应该允许负数,但不管我如何尝试,它只允许 >0

public class SalesActivitySubmission
{
    ...

    [Column(TypeName = "money")]
    [Range(-999999999999.99, 999999999999.99, ErrorMessage = "Please enter a positive amount of 999,999,999,999.99 or less.")]
    public decimal? Amount { get; set; }

    ...
}
public class SalesActivitySubmissionValidator : AbstractValidator<SalesActivitySubmission>
{
    public SalesActivitySubmissionValidator()
    {
        ...

        RuleFor(s => s.Amount).InclusiveBetween(0, 999999999999)
            .WithMessage("Amount must be within 0 and 999999999999, inclusively.");

        ...
    }
}

通常,您在使用 fluentvalidation 时不会指定数据属性 - 如果可能,您应该有一个单一的真实来源进行验证。

在 ASP.NET Core 中,fluentvalidation 在其他验证提供程序之前执行。 在这种情况下,内置提供程序将没有机会测试负数量的范围验证数据属性,因为 fluentvalidation 将已经测试该属性并返回验证失败。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM