Here is my solution. It is not perfect, but it is a start.
First of all, I have created an enum called ErrorLevel
:
enum ErrorLevel
{
Error,
Warning
}
Next, for every validation rule, I have created a Custom Attribute. Each Custom Attributes has a private field ErrorLevel
, and a Validate
method which returns the error description or null if no error was found (I created an IValidationRule<T>
interface to ensure that this method exists).
IValidationRule<T>:
interface IValidationRule<T>
{
string Validate(T input);
}
NoNumbersAttribute:
class NoNumbersAttribute : Attribute, IValidationRule<Person>
{
private ErrorLevel errorLevel;
public NoNumbersAttribute(ErrorLevel errorLevel)
{
this.errorLevel = errorLevel;
}
public string Validate(Person person)
{
if(person.Name.Any(c => char.IsDigit(c)))
{
return $"{errorLevel.ToString()}: 'Name' contains numeric characters. ";
}
return null;
}
}
NoSpecialCharactersAttribute:
class NoSpecialCharactersAttribute : Attribute, IValidationRule<Person>
{
private ErrorLevel errorLevel;
public NoSpecialCharactersAttribute(ErrorLevel errorLevel)
{
this.errorLevel = errorLevel;
}
public string Validate(Person person)
{
if (!new Regex("^[a-zA-Z0-9 ]*$").IsMatch(person.Name))
{
return $"{errorLevel.ToString()}: 'Name' contains special character.";
}
return null;
}
}
Notice that we use the errorLevel
to ensure the output will contain the correct error level.
Now, in the Person
class I changed the fields to properties since you really do not want public fields. I have added the Custom Attributes on top of the relevant properties:
Person:
class Person
{
[NoSpecialCharacters(ErrorLevel.Error)]
[NoNumbers(ErrorLevel.Warning)]
public string Name { get; }
public int Age { get; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
And finally, I have created a method that gets a list of persons, gets their validation results and print them:
ValidatePeople:
static bool ValidatePeople(List<Person> people)
{
bool isValid = true;
foreach (Person person in people)
{
PropertyInfo[] properties = typeof(Person).GetProperties();
foreach (PropertyInfo property in properties)
{
var rules = Array.ConvertAll(property.GetCustomAttributes(typeof(IValidationRule<Person>), true), item => (IValidationRule<Person>)item);
var failures = rules.Select(r => r.Validate(person)).Where(f => f != null);
failures.ToList().ForEach(f => Console.WriteLine($"({person.Name}) {f}"));
if (failures.Count() > 0) isValid = false;
}
}
return isValid;
}
So the Main
method will look something like this:
static void Main(string[] args)
{
List<Person> people = GetPeopleFromCSV();
if(ValidatePeople(people))
{
InsertPeopleToDatabase();
}
}