かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

EnterpriseLibrary 6のValidation Application Blockを触ってみた

恐らく、一番機能が豊富だと思われるオブジェクトの検証機能を持ったライブラリです。標準のDataAnnotationsよりも、機能は多い(ともに使うこともできる)です。

個人的にプロパティに属性を追加して使うのが好みなので、その使い方について紹介したいと思います。

使い方

NuGetでEnterpriseLibrary Validationあたりで検索して"EnterpriseLibrary - Validation Application Block"を追加します。

Microsoft.Practices.EnterpriseLibrary.Validation.Validators名前空間にある各種****ValidatorAttributeを使ってプロパティに検証の条件を追加します。例えば名前が必須入力で、1~10文字の間の場合は以下のように定義します。

public class Person
{
    [NotNullValidator(MessageTemplate = "名前を入力してください")]
    [StringLengthValidator(1, 10, MessageTemplate = "名前は1~10文字です")]
    public string Name { get; set; }
}

バリデーションを行うには以下のようなコードになります。

// バリデーターを作成する
var validator = ValidationFactory.CreateValidator<Person>();
// 引数で渡されたオブジェクトの検証
var validationResults = validator.Validate(new Person());

// 検証結果を確認
Console.WriteLine(validationResults.IsValid); // False

// OKなケース
var validationResults2 = validator.Validate(new Person { Name = "tanaka" });
Console.WriteLine(validationResults2.IsValid); // True

プロパティを増やして、属性をつけることでValidateメソッドで全プロパティの検証結果がValidationResultsという形で返ってきます。

込み入ったオブジェクトの検証

これが、ほかの奴であまり見ない機能です。例えば、先ほどのPersonクラスの名前をFirstNameとLastNameを持ったオブジェクトにしてみます。

// 名前を表すオブジェクト
public class NameObject
{
    [NotNullValidator(MessageTemplate = "FirstNameは必須です")]
    public string FirstName { get; set; }

    [NotNullValidator(MessageTemplate = "LastNameは必須です")]
    public string LastName { get; set; }
}

// NameObjectを持つオブジェクト
public class Person
{
    [NotNullValidator(MessageTemplate = "名前は必須です")]
    // ObjectValidatorをつけるとオブジェクトのプロパティも妥当性検証の対象になる
    [ObjectValidator]
    public NameObject Name { get; set; }
}

ObjectValidatorをつけると、NameObjectの中も妥当性検証を行ってくれるようになります。

// バリデーターを作成する
var validator = ValidationFactory.CreateValidator<Person>();
// 引数で渡されたオブジェクトの検証
var validationResults = validator.Validate(new Person());

// 検証結果を確認
Console.WriteLine(validationResults.IsValid); // False

// OKなケース
var validationResults2 = validator.Validate(new Person 
{ 
    Name = new NameObject 
    { 
        FirstName = "taro", 
        LastName = "tanaka" 
    } 
});
Console.WriteLine(validationResults2.IsValid); // True

エラー情報を取得する

エラーの情報の取得はValidationResultsにコレクションとして入ってるのでループ回したりしてとることができます。例えば列挙する場合は以下のようになります。

// バリデーターを作成する
var validator = ValidationFactory.CreateValidator<Person>();
// 引数で渡されたオブジェクトの検証
var validationResults = validator.Validate(new Person { Name = new NameObject() });

// 検証結果を確認
Console.WriteLine(validationResults.IsValid); // False

foreach (var r in validationResults)
{
    Console.WriteLine("プロパティ {0}: メッセージ {1}", r.Key, r.Message);
}

実行すると以下のようになります。

プロパティ FirstName: メッセージ FirstNameは必須です
プロパティ LastName: メッセージ LastNameは必須です

これは、全部のプロパティのエラーが入ってるので、特定のプロパティのみ検証したい場合は、PropertyValueValidatorを使います。こいつは属性じゃなくて、手組で検証ルールを組まないといけないです。例えばNullじゃなくてオブジェクトの検証をやる場合は以下のようになります。

var propertyValidator = new PropertyValueValidator<Person>("Name", 
    new AndCompositeValidator(
        new NotNullValidator(),
        new ObjectValidator())
    );
// 引数で渡されたオブジェクトの検証
var validationResults = propertyValidator.Validate(new Person { Name = new NameObject() });

// 検証結果を確認
Console.WriteLine(validationResults.IsValid); // False

foreach (var r in validationResults)
{
    Console.WriteLine("プロパティ {0}: メッセージ {1}", r.Key, r.Message);
}

これでNameプロパティのみ検証が行われます。

プロパティにつけた属性を有効活用したい場合は、全体を検証したあとLINQあたりでしぼったりしないといけないと思います。

// バリデーターを作成する
var validator = ValidationFactory.CreateValidator<Person>();
// 引数で渡されたオブジェクトの検証
var validationResults = validator.Validate(new Person { Name = new NameObject() });

// 検証結果を確認
Console.WriteLine(validationResults.IsValid); // False

var result = validationResults.FirstOrDefault(r => r.Key == "Name");
if (result == null)
{
    // エラーがない
}
else
{
    // エラーがある
}

まとめ

とまぁ走り書きですが、結構豊富な機能があるんじゃないんでしょうか。