C
C#16mo ago
TJacken

❔ Validate list of objects after reading excel file

Hi all, how would you validate data from excel file with fluent validation? I have method which will read data from excel file and return me list of person objects. After read I call validator to validate data from excel file. Person email must be unique, so it shouldn't exist in database when I read data and it shouldn't be duplicated in excel file. Is it good approach to inject repository in validator or just collect all emails as list and pass it to validator? If I use that approach what if someone else insert person with email in parallel with my operation? Person class:
public class Person
{
public string FullName { get; set; }

public string Email { get; set; }

public string City { get; set; }
}
public class Person
{
public string FullName { get; set; }

public string Email { get; set; }

public string City { get; set; }
}
Person validator class:
public class PersonValidator : AbstractValidator<Person>
{
private readonly IPersonRepository _personRepository;
private readonly List<Person> _peopleFromExcelFile;

public PersonValidator(IPersonRepository personRepository, List<Person> peopleFromExcelFile)
{
_personRepository = personRepository;
_peopleFromExcelFile = peopleFromExcelFile;

RuleFor(x => x.FullName)
.NotEmpty()
.WithMessage("Full name is empty!");

RuleFor(x => x.Email)
.NotEmpty()
.WithMessage("Email is empty");

RuleFor(x => x.Email)
.Must(PersonMailDoNotExistInRepository)
.WithMessage("Email already exists in database");

RuleFor(x => x.Email)
.Must(EmailIsNotDuplicateInExcelFile)
.WithMessage("Email is duplicated in excel file");
}

private bool PersonMailDoNotExistInRepository(string email)
{
return _personRepository.GetByEmail(email) == null;
}

private bool EmailIsNotDuplicateInExcelFile(string email)
{
return _peopleFromExcelFile.Count(x => x.Email == email) == 1;
}
}
public class PersonValidator : AbstractValidator<Person>
{
private readonly IPersonRepository _personRepository;
private readonly List<Person> _peopleFromExcelFile;

public PersonValidator(IPersonRepository personRepository, List<Person> peopleFromExcelFile)
{
_personRepository = personRepository;
_peopleFromExcelFile = peopleFromExcelFile;

RuleFor(x => x.FullName)
.NotEmpty()
.WithMessage("Full name is empty!");

RuleFor(x => x.Email)
.NotEmpty()
.WithMessage("Email is empty");

RuleFor(x => x.Email)
.Must(PersonMailDoNotExistInRepository)
.WithMessage("Email already exists in database");

RuleFor(x => x.Email)
.Must(EmailIsNotDuplicateInExcelFile)
.WithMessage("Email is duplicated in excel file");
}

private bool PersonMailDoNotExistInRepository(string email)
{
return _personRepository.GetByEmail(email) == null;
}

private bool EmailIsNotDuplicateInExcelFile(string email)
{
return _peopleFromExcelFile.Count(x => x.Email == email) == 1;
}
}
5 Replies
Pobiega
Pobiega16mo ago
I personally only use fluent validation for basic validation like "does it match the expected input format" or "is this int between the valid lower and upperbound" and leave business validation (like email must be unique) to.. well, whatever service/command uses this data. That said, how many emails are we talking? Making a DB query for each and every row in the excel file might be fine, or it might be insanity - depends on the size of the excel file and DB
TJacken
TJackenOP16mo ago
Excel file had approximately 100-200 different emails, but this numbers can increase in future. Would you have some separate service to validate data or?
Why don't you think fluent validation wouldn't be good fit for this scenario?
Pobiega
Pobiega16mo ago
Fluent isn't a bad fit here, its just that you are mixing two kinds of validation currently - basic "is this string valid" validation and business validation "is this string not already present in my database" The database check is several orders of magnitude more expensive than everything else here, so ideally we want to avoid doing it for as long as possible, meaning only check it if everything else is valid
TJacken
TJackenOP16mo ago
Could I create PersonValidationService class which will have field private readonly PersonValidator _personValidator, and this person validator will only have those rules:
RuleFor(x => x.Email)
.Must(PersonMailDoNotExistInRepository)
.WithMessage("Email already exists in database");

RuleFor(x => x.Email)
.Must(EmailIsNotDuplicateInExcelFile)
.WithMessage("Email is duplicated in excel file");
RuleFor(x => x.Email)
.Must(PersonMailDoNotExistInRepository)
.WithMessage("Email already exists in database");

RuleFor(x => x.Email)
.Must(EmailIsNotDuplicateInExcelFile)
.WithMessage("Email is duplicated in excel file");
this service will not have PersonRepository instead I will inject list of emails from database and all emails from excel file (List<string> emailsFromDb, List<string> emailsFromFile).
Accord
Accord16mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server