I am trying to create a custom complex type validation. I have created a type that has three child classes each of which has a custom validator. The Validator just goes to the defaults. One specific example is that the money amounts are supposed to allow negative numbers, but regardless of my attempts, it only allows >0. Any help is appreciated.
Razor Page
<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();
}
}
Extension
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;
}
Parent Class
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();
}
Parent Valdator
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());
}
}
Child Classes
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; }
}
Child Validators
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.");
}
}