コンソールプロジェクトでCSVファイルから入力を読み取り、それらをデータベースに保存しようとしています。
そのために、CSV行をマップするクラスPerson
を作成しました。
CSVファイルには、2つの列Name
とAge
があります。 Person
クラスは似ています。
class Person
{
public string Name;
public int Age;
}
したがって、すべての設定されたオブジェクトのリストはList<Person>
。
設定されたオブジェクトをデータベースに保存する前に、コンソールに検証メッセージを表示するという新しい要件があります。
検証には、ErrorとWarningの2つのレベルがあります。
たとえば、Name
プロパティに特殊文字が含まれている場合、次のメッセージを表示する必要があります:「エラー:名前に特殊文字が含まれています」
Name
に数字が適切に含まれている場合、警告メッセージのみを表示する必要があります:「警告:名前に数字が含まれています」
DataAnnotation の使用を考えていましたが、検証プロセスに異なるレベル(エラーと警告)を追加する方法がわかりません。また、DataAnnotationがWebアプリケーションのみに適合するかどうかもわかりません。
各プロパティに対してこの検証を実行するために、Person
クラスにいくつかの機能を追加する方法はありますか?
注:これは質問をよりよく理解するための例にすぎません。他のプロパティには他のルールがあります。
これが私の解決策です。完璧ではありませんが、それは始まりです。
まず、ErrorLevel
という列挙型を作成しました。
enum ErrorLevel
{
Error,
Warning
}
次に、すべての検証ルールについて、カスタム属性を作成しました。各カスタム属性には、プライベートフィールドErrorLevel
と、エラーの説明を返すValidate
メソッド、またはエラーが見つからなかった場合はnullがあります(IValidationRule<T>
このメソッドが存在することを確認するためのインターフェイス)。
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;
}
}
errorLevel
を使用して、出力に正しいエラーレベルが含まれていることを確認します。
ここで、Person
クラスで、パブリックフィールドが本当に必要ないため、フィールドをプロパティに変更しました。関連するプロパティの上にカスタム属性を追加しました:
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;
}
}
そして最後に、人のリストを取得し、その検証結果を取得して出力するメソッドを作成しました。
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;
}
したがって、Main
メソッドは次のようになります。
static void Main(string[] args)
{
List<Person> people = GetPeopleFromCSV();
if(ValidatePeople(people))
{
InsertPeopleToDatabase();
}
}
あなたが行くことができるいくつかの方法があります。
ビジネスオブジェクトは、次のように定義できるIValidateを実装できます。
public interface IValidate
{
List<IWarning> GetWarnings();
List<IError> GetErrors();
}
したがって、クラスに検証を追加します。
次のようなバリデータを作成できます
public abstract class Validator
{
public abstract List<IWarning> GetWarnings();
public abstract List<IError> GetErrors();
}
public class PersonValidator : Validator
{
private IPerson _Person;
public PersonValidator(IPerson person)
{
_Person = person;
}
public override List<IWarning> GetWarnings() { /* logic here */ }
public override List<IError> GetErrors() { /* logic here */ }
}
バリデーターでロジックを実装すると、DataAnnotations、またはアノテーションの独自の属性を読み取ることもできます。
つまりレベルを示す独自の注釈を作成できます。
[AttributeUsage(AttributeTargets.Property)]
public class MyValidationAttribute : Attribute
{
public Level Level {get;} // error, warning, ...
public string AllowedCharacters {get;}
// don't forget the ctor
}