web-dev-qa-db-ja.com

クラスプロパティを検証する最良の方法

バックグラウンド:

CSVファイルがあり、各行の各要素を準備して検証し、有効なデータを持つクラスのコレクションを作成する必要があります。

つまり、CSVファイルは次のようになります。

   EmpID,FirstName,LastName,Salary
    1,James,Help,100000
    2,Jane,Scott,1000
    3,Mary,Fraze,10000

クラスは次のようになります。

public class Employees
{
    public int EmpID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Salary { get; set; }
    public string ErrorReason { get; set; }
}

各フィールドに必要な検証は次のとおりです。

  • EmpID:

    • その必須フィールドなので、nullや空にすることはできません
    • 整数のみである必要があります
    • 2桁以下にする必要があります
    • データベースに存在する必要があります(そのデータベースにクエリを実行し、従業員がこのempidで終了するかどうかを確認します。
  • FirstName(LastNameと同じ検証):

    • その必須フィールドなので、nullや空にすることはできません
    • アルファベットのみである必要があります。
    • 30文字以下にしてください
  • 給料:

    • その必須フィールドなので、nullや空にすることはできません
    • 10進数でなければなりません。

これを達成するために、これが私のアプローチです:

  1. 行ごとにCSVファイルを読み取る
  2. 各要素についてEmpId、FirstName ...など検証ロジックを持つ個々のメソッドを呼び出すことにより、必要な検証を実行します。例:public bool ValidateIsDecimal(string value){} public bool ValidateIsEmpIdExists(string value){}等
  3. 有効な場合、「従業員」クラスの対応するプロパティを入力します。
  4. 有効でない場合は、検証の失敗の原因に関する適切な理由を使用して、「ErrorReason」プロパティを入力します(例:必要なフィールドが欠落しているか、データ型が10進数ではないなど)。
  5. このクラスをEmployeesコレクションに追加します(例:リスト)

だから、私の質問は、これが正しいアプローチなのか、それともクラスのプロパティを検証する他のより良い/よりクリーンな方法があるのか​​ということです。

5
user2697452

データを入力してエラーをキャプチャする必要がある場合、私はおそらく次のようなことをするでしょう:

public class Employee
{
    public int EmpID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Salary { get; set; }
}

public class ErrorEmployee : Employee
{
    public string[] ErrorReason { get; set; }
}
  1. 行ごとにCSVファイルを読み取る
  2. 各要素、つまりEmpId、FirstName ...などについて、検証ロジックを持つ個々のメソッドを呼び出すことにより、必要な検証を実行します。例:public bool ValidateIsDecimal(string value){} public bool ValidateIsEmpIdExists(string value){}など
  3. 有効な場合、「従業員」クラスの対応するプロパティを入力します。このクラスをEmployeesコレクションに追加します(例:リスト)
  4. 有効でない場合は、検証が失敗した原因に関する適切な理由で "ErrorReason"プロパティを入力します(例:必要なフィールドが欠落しているか、データ型が10進数ではないなど)このクラスをErrorEmployeesコレクションに追加(例:リスト)

エラーメッセージで通常のEmployee(単数形、それらのコレクションには複数形を使用)オブジェクトを汚染したくありません。上記のメモのように、FluentValidationライブラリをチェックして検証を確認し、最初に見つかったエラーだけでなく、エラーのリストをキャプチャします。

2
Larry Smithmier

これは非常に単純な問題なので、これで間違ってしまうことはほとんどありません。あなたのアプローチはうまく見えます。

私が悪いことに気づいたのは、行ごとにDBをクエリしているという事実だけです。すべてのIDを使用してDBを1回だけクエリした方がよいでしょう。ただし、100行のみが可能であると仮定すると、これは問題になりません。しかし、私は少なくともコメントを書くでしょう、これは将来的に問題になるかもしれません。

3
Euphoric

3つのクラス

  • CSVLineまたはstring[]
  • 従業員
  • バリデーター

CSVLine:

{
    public int LineNumber {get; set;}
    public string EmpID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Salary { get; set; }
}
  • 行をCSVLineに読み込みます
  • 必要に応じて、CSVLineプロパティまたは配列要素で適切なValidatorメソッドを呼び出します
  • 有効な場合、CSVLineまたは文字列配列で渡す従業員を作成します

有効でない場合は、CSV行と検証エラーを拒否ファイルに記録できます。すべての属性を検証するか、すぐに失敗して最初のエラーで停止することができます。あなた次第。また、CSVLine内の名前付きフィールドが不要で、必要に応じて配列要素をバリデーターに渡すだけの場合は、CSVをstring []のような一般的な構造にする機会があります。配列の要素数(この場合は4)も検証できるため、配列の方が適している場合があります。

バリデーターをインターフェースにして、さまざまなタイプのバリデーターを作成することもできます。または、これが処理する唯一のファイルである場合は、クラスを1つだけ持つことができます。

2
Jon Raynor

リストされていない追加の合併症がない限り、それはかなり簡単です。これと同じくらい簡単なこと(疑似コード)が行います:

var customers = File.ReadAllLines("...")
    .AsParallel()
    .Select(l => l.Split(new char[] { ',' }))
    .Where(s => Tuple.Create(
        validateId(s[0]), 
        validateFirstName(s[1]), 
        validateLastName(s[2]),  
        validateSalary(s[3])))
    .Select(t => new Customer(t.Item1, t.Item2, t.Item3, t.Item4));

ここでの唯一のニュアンスは、validateXYZが正しいタイプも検証して返すということです。

「ErrorReason」に関する限り、不良データを入力してから、不良データの理由を考えるのは悪い考えのようです。

数百万行が数秒で完了するファイルに対して、この正確な方法を使用して、これよりも複雑なクエリを記述しました。それよりも多くのデータがある場合は、実行する作業が増える可能性があります。

(ところで:これらのタイプの問題では、linqpadがあなたの友達です)。

1
Steven Evers