web-dev-qa-db-ja.com

C#でのマルチレベル検証

コンソールプロジェクトでCSVファイルから入力を読み取り、それらをデータベースに保存しようとしています。

そのために、CSV行をマップするクラスPersonを作成しました。

CSVファイルには、2つの列NameAgeがあります。 Personクラスは似ています。

class Person
{
    public string Name;
    public int Age;
}

したがって、すべての設定されたオブジェクトのリストはList<Person>

設定されたオブジェクトをデータベースに保存する前に、コンソールに検証メッセージを表示するという新しい要件があります。

検証には、ErrorWarningの2つのレベルがあります。

たとえば、Nameプロパティに特殊文字が含まれている場合、次のメッセージを表示する必要があります:「エラー:名前に特殊文字が含まれています」

Nameに数字が適切に含まれている場合、警告メッセージのみを表示する必要があります:「警告:名前に数字が含まれています」

DataAnnotation の使用を考えていましたが、検証プロセスに異なるレベル(エラーと警告)を追加する方法がわかりません。また、DataAnnotationがWebアプリケーションのみに適合するかどうかもわかりません。

各プロパティに対してこの検証を実行するために、Personクラスにいくつかの機能を追加する方法はありますか?

注:これは質問をよりよく理解するための例にすぎません。他のプロパティには他のルールがあります。

4
Mhd

これが私の解決策です。完璧ではありませんが、それは始まりです。

まず、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();
    }  
}
5
Sipo

あなたが行くことができるいくつかの方法があります。

ビジネスオブジェクトは、次のように定義できる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 
}
1
Bernhard Hiller