2

I've made a rule that says "If Category is 'IT' or 'Part', either Username or Description must not be null or empty'.

But because I'd rather not rely on string literals for the returning the names of the required fields and strongly type them instead, I used Ilya Ivanov's answer from Using FluentValidation's WithMessage method with a list of named parameters. (I know it's not great that I have to account for the enum in a funny way.)

What I'm most interested in now is two things:

A: Should there be greater 'cohesion' between SomeUserInformationMustNotBeEmpty and CreateErrorMessage? Right now, I could accidentally validate on another property and then forget to add it to the message (or vice versa)?

B: Can the error message be dynamically generated from the validation itself (this is obviously the case for simpler rules that don't even need a WithMessage)?

So my question is: Is there any way to use a Custom Property Validator or something similar to validate and return an error message depending on what properties are passed as 'required' (i.e. !String.IsNullorEmpty(property))?

Maybe something like

RuleFor(d => d.Category)
              .MustEnforceAtLeastOneNonEmpty(ListOfProperties)
              .When(x => x.Category.IsIn(Category.Part, Category.IT))

which would both validate and return a message against the current rule:

"You need to enter a Username or Description when the category is IT" 

And, if I added 'Age' to to the list of properties that are required, it would validate and return the message:

"You need to enter a Username, Age or Description when the category is IT"

Or maybe even:

"You need to enter a Username, Age or Description when the category is IT or Part"

My current view model:

[Validator(typeof(LeaveRequestValidator))]
public class LeaveRequestModel
{
    public string Username { get; set; }

    public Category Category { get; set; }

    public string Description { get; set; }

    public int Age { get; set; }
}

Category is an enum:

public enum Category
{
    HR = 1,
    Finance = 2,
    Part = 3,
    IT = 4
}


public class LeaveRequestValidator : AbstractValidator<LeaveRequestModel>
{
    public LeaveRequestValidator()
    {

        // Option One. A single line and a private bool. 
        RuleFor(d => d)
                       .Must(SomeUserInformationMustNotBeEmpty)
                       .When(x => x.Category.IsIn(Category.Part, Category.IT))
                       .WithMessage("{0}", CreateErrorMessage);


    private bool SomeUserInformationMustNotBeEmpty(LeaveRequestModel leaveRequestModel)
    {
        return (!String.IsNullOrEmpty(leaveRequestModel.Username) || !String.IsNullOrEmpty(leaveRequestModel.Description));
    }

    private string CreateErrorMessage(LeaveRequestModel leaveRequestModel)
    {
        string requiredPropertyOne = ModelMetadata.FromLambdaExpression<LeaveRequestModel, string>(x => x.Username, new ViewDataDictionary<LeaveRequestModel>()).DisplayName;
        string requiredPropertyTwo = ModelMetadata.FromLambdaExpression<LeaveRequestModel, string>(x => x.Description, new ViewDataDictionary<LeaveRequestModel>()).DisplayName;
        string checkedPropertyTwo = LeaveRequestModel.Category.GetType().Name.ToString();
        return String.Format("You need to enter a {0} or {1} when the {2} is {3}.", requiredPropertyOne, requiredPropertyTwo, checkedPropertyTwo, LeaveRequestModel.Category);
    }
}

I used a small extension to search the enums of Category.

public static class Extensions
{
    public static bool IsIn<T>(this T value, params T[] list)
    {
        return list.Contains(value);
    }
}
Community
  • 1
  • 1

0 Answers0