Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Validating against a string containing comma delimited emails

I'm trying to validate this property in MVC model, which can contain zero or more email addresses delimited by comma:

public class DashboardVM
{
    public string CurrentAbuseEmails { get; set; }
    ...
}

The question is how do I do this using the built-in fluent validation rule for Email Address? For now I have a solution using Must and regular expression which works, but I don't find it .. elegant enough.

    public DashboardVMValidator()
    {
        RuleFor(x => x.CurrentAbuseEmails).Must(BeValidDelimitedEmailList).WithMessage("One or more email addresses are not valid.");
    }

    private bool BeValidDelimitedEmailList(string delimitedEmails)
    {
        //... match very very long reg. expression
    }

So far the closest solution including RuleFor(...).EmailAddress() was creating a custom Validator below and call Validate on each email from the string, but that didn't work for some reason (AbuseEmailValidator wasn't able to get my predicate x => x - when calling validator.Validate on each email).

public class AbuseEmailValidator : AbstractValidator<string>
{
    public AbuseEmailValidator()
    {
        RuleFor(x => x).EmailAddress().WithMessage("Email address is not valid");
    }
}

Is there way to do this in some simple manner? Something similar to this solution, but with one string instead of list of strings, as I can't use SetCollectionValidator (or can I?): How do you validate against each string in a list using Fluent Validation?

like image 502
Matej Avatar asked Sep 07 '12 14:09

Matej


2 Answers

You can try something like this:

public class InvoiceValidator : AbstractValidator<ContractInvoicingEditModel>
{
    public InvoiceValidator()
    {
        RuleFor(m => m.EmailAddressTo)
            .Must(CommonValidators.CheckValidEmails).WithMessage("Some of the emails   provided are not valid");
    }
}

public static class CommonValidators
{
    public static bool CheckValidEmails(string arg)
    {
        var list = arg.Split(';');
        var isValid = true;
        var emailValidator = new EmailValidator();

        foreach (var t in list)
        {
            isValid = emailValidator.Validate(new EmailModel { Email = t.Trim() }).IsValid;
            if (!isValid)
                break;
        }

        return isValid;
    }
}
public class EmailValidator : AbstractValidator<EmailModel>
{
    public EmailValidator()
    {
        RuleFor(x => x.Email).EmailAddress();
    }
}

public class EmailModel
{
    public string Email { get; set; }
}

It seems to work fine if you use an intermediary poco. My emails are separated by ";" in this case. Hope it helps.

like image 196
MihaiPopescu Avatar answered Sep 30 '22 11:09

MihaiPopescu


As of version 9, FluentValidation supports this without requiring custom validators using the Transform and ForEach methods.

In versions 9.0-9.4, you would write it like this:

RuleFor(x => x.List)
    .Transform(list => list.Split(','))
    .ForEach(itemRule => itemRule.EmailAddress());

In version 9.5 and up, RuleFor isn't used with Transform, so you write it like this:

Transform(x => x.List, list => list.Split(','))
    .ForEach(itemRule => itemRule.EmailAddress());

To handle nulls, use null coalesce operator in the Transform delegate:

list => (list ?? "").Split(',')

To handle whitespace, you may want to trim each item in the list. You can add a Select clause:

list => (list ?? "").Split(',')
    .Select(item => item.Trim())

If you want to ignore empty items, add a Where clause:

list => (list ?? "").Split(',')
    .Select(item => item.Trim())
    .Where(item => !string.IsNullOrEmpty(item))

To require that there is at least one item in the list, add the NotEmpty rule. So the final version 9.5+ code will look like:

Transform(x => x.List, 
    list => (list ?? "").Split(',')
        .Select(item => item.Trim())
        .Where(item => !string.IsNullOrEmpty(item)))
    .NotEmpty()
    .ForEach(itemRule => itemRule.EmailAddress());
like image 30
Jack A. Avatar answered Sep 30 '22 13:09

Jack A.