I'm attempting to adapt fluentvalidation (3.3) to work with our current Entity validation.
An Entity has a collection of type "ValidationResult" associated with it.
public class Entity { . . IEnumerable<ValidationResult> ValidationResults { get; } bool IsValid (IValidator validator) { /* Clear then Populate Validation Results */ } }
Where ValidationResult looks like this:
public enum ValidationType { None, InputValiation, BusinessRule } public enum ValidationSeverity { None, Info, Warning, Error } public class ValidationResult { public string Key { get; private set; } public ValidationType ValidationType { get; private set; } public ValidationSeverity ValidationSeverity { get; private set; } public string UserMessage { get; private set; } public string Property { get; private set; } }
Note that the validation result can be either a Input validation error or a Business Validation error. As well, the severity can be at different levels. This allows us to create "business validation results" that are not necessarily show stoppers (e.g. Warning - Customer Order total is larger than specified maximum).
As a result, I have attempted to create an FluentValidation extension that allows me to set additional context on the rules so that the Validation type and Severity can be set (usage):
public class CustomerOrderValidator : AbstractValidator<CustomerOrder> { public CustomerOrderValidator() { RuleFor(x => x.Total).LessThanOrEqualTo(200.00M).AddsEntityValidationResult(ValidationType.BusinessRule, ValidationSeverity.Warning); } }
So far I have a "hack" extension that seemed to be working OK until I ran into the need to validate child collections at which point it all fell apart. I was hoping that someone might be able to provide some guidance / suggestions. At this point I really can't change what we have for the validation results in our domain.
Note: My original thought was to get Fluent Validation results and use an "adapter" to convert them to our Entity Validation Results. However, there would be know way to determine the results context (Severity & Type).
Here is what my current extension looks like:
public static IRuleBuilderOptions<T, TProperty> AddsEntityValidationResult<T, TProperty>(this IRuleBuilderOptions<T, TProperty> rule, ValidationType validationType, ValidationSeverity validationSeverity) where T : IEntity { rule.Configure(config => config.OnFailure = new Action<T>(x => { var args = config.CurrentValidator.CustomMessageFormatArguments.Select(a => a.Invoke(x)).ToArray(); var errorMessage = args.Count() > 0 ? string.Format(config.CurrentValidator.ErrorMessageSource.GetString(), args) : config.CurrentValidator.ErrorMessageSource.GetString(); errorMessage = errorMessage.Replace("{PropertyName}", config.PropertyName); x.AddValidationResult( new ValidationResult(Guid.NewGuid().ToString(), validationType, validationSeverity, config.PropertyName, errorMessage)); }).CoerceToNonGeneric()); return rule; }
Thanks for your help!
Note: To be more concise, what I really need to be able to do is add additional "context" to a Rule when it's defined. If I could do that then I could use an "adapter" on the Fluent Validation results to create the Entity Validation Results using the correct context (i.e. Validation Severity & Type). I do not necessarily have to have an extension that "adds" the results on failure.