198

IValidatableObjectこれは、プロパティを相互に比較できるようにオブジェクトを検証するために使用されることを理解しています。

個々のプロパティを検証するための属性が必要ですが、特定の場合に一部のプロパティの失敗を無視したいと思います。

以下の場合、間違って使用しようとしていますか?そうでない場合、これをどのように実装しますか?

public class ValidateMe : IValidatableObject
{
    [Required]
    public bool Enable { get; set; }

    [Range(1, 5)]
    public int Prop1 { get; set; }

    [Range(1, 5)]
    public int Prop2 { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (!this.Enable)
        {
            /* Return valid result here.
             * I don't care if Prop1 and Prop2 are out of range
             * if the whole object is not "enabled"
             */
        }
        else
        {
            /* Check if Prop1 and Prop2 meet their range requirements here
             * and return accordingly.
             */ 
        }
    }
}
4

8 に答える 8

190

まず、適切なリソースを紹介してくれた@ paper1337に感謝します...私は登録されていないため、投票できません。他の誰かがこれを読んだ場合は、投票してください。

これが私がやろうとしていたことを達成する方法です。

検証可能なクラス:

public class ValidateMe : IValidatableObject
{
    [Required]
    public bool Enable { get; set; }

    [Range(1, 5)]
    public int Prop1 { get; set; }

    [Range(1, 5)]
    public int Prop2 { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var results = new List<ValidationResult>();
        if (this.Enable)
        {
            Validator.TryValidateProperty(this.Prop1,
                new ValidationContext(this, null, null) { MemberName = "Prop1" },
                results);
            Validator.TryValidateProperty(this.Prop2,
                new ValidationContext(this, null, null) { MemberName = "Prop2" },
                results);

            // some other random test
            if (this.Prop1 > this.Prop2)
            {
                results.Add(new ValidationResult("Prop1 must be larger than Prop2"));
            }
        }
        return results;
    }
}

Validator.TryValidateProperty()検証に失敗した場合、を使用すると結果コレクションに追加されます。失敗した検証がない場合、成功を示す結果コレクションには何も追加されません。

検証の実行:

    public void DoValidation()
    {
        var toValidate = new ValidateMe()
        {
            Enable = true,
            Prop1 = 1,
            Prop2 = 2
        };

        bool validateAllProperties = false;

        var results = new List<ValidationResult>();

        bool isValid = Validator.TryValidateObject(
            toValidate,
            new ValidationContext(toValidate, null, null),
            results,
            validateAllProperties);
    }

validateAllPropertiesこのメソッドが機能するには、falseに設定することが重要です。validateAllPropertiesfalseの場合、[Required]属性を持つプロパティのみがチェックされます。これにより、IValidatableObject.Validate()メソッドが条件付き検証を処理できるようになります。

于 2010-08-03T22:14:45.440 に答える
85

バリデーターを使用した検証オブジェクトとプロパティに関するJeffHandleyのブログ投稿からの引用:

オブジェクトを検証するとき、次のプロセスがValidator.ValidateObjectに適用されます。

  1. プロパティレベルの属性を検証する
  2. いずれかのバリデーターが無効な場合は、検証を中止して失敗を返します
  3. オブジェクトレベルの属性を検証します
  4. いずれかのバリデーターが無効な場合は、検証を中止して失敗を返します
  5. デスクトップフレームワーク上にあり、オブジェクトがIValidatableObjectを実装している場合は、そのValidateメソッドを呼び出して、失敗を返します。

これは、ステップ2で検証が中止されるため、実行しようとしていることがそのままでは機能しないことを示しています。組み込みの属性を継承する属性を作成し、通常の検証を実行する前に、有効なプロパティの存在を(インターフェイスを介して)具体的に確認することができます。または、エンティティを検証するためのすべてのロジックをValidateメソッドに配置することもできます。

ここValidatorでクラスの正確な実装を確認することもできます

于 2010-08-03T20:44:45.013 に答える
39

いくつかの点を追加するだけです:

Validate()メソッド シグネチャが を返すためIEnumerable<>yield return結果を遅延生成するために使用できます。これは、検証チェックの一部が IO または CPU を集中的に使用する場合に役立ちます。

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
    if (this.Enable)
    {
        // ...
        if (this.Prop1 > this.Prop2)
        {
            yield return new ValidationResult("Prop1 must be larger than Prop2");
        }

また、 を使用している場合はMVC ModelState、検証結果の失敗を次のようにエントリに変換できます (これは、カスタム モデル バインダーModelStateで検証を行っている場合に役立つ場合があります)。

var resultsGroupedByMembers = validationResults
    .SelectMany(vr => vr.MemberNames
                        .Select(mn => new { MemberName = mn ?? "", 
                                            Error = vr.ErrorMessage }))
    .GroupBy(x => x.MemberName);

foreach (var member in resultsGroupedByMembers)
{
    ModelState.AddModelError(
        member.Key,
        string.Join(". ", member.Select(m => m.Error)));
}
于 2014-03-26T05:47:29.067 に答える
5

検証用の一般的な使用法抽象クラスを実装しました

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace App.Abstractions
{
    [Serializable]
    abstract public class AEntity
    {
        public int Id { get; set; }

        public IEnumerable<ValidationResult> Validate()
        {
            var vResults = new List<ValidationResult>();

            var vc = new ValidationContext(
                instance: this,
                serviceProvider: null,
                items: null);

            var isValid = Validator.TryValidateObject(
                instance: vc.ObjectInstance,
                validationContext: vc,
                validationResults: vResults,
                validateAllProperties: true);

            /*
            if (true)
            {
                yield return new ValidationResult("Custom Validation","A Property Name string (optional)");
            }
            */

            if (!isValid)
            {
                foreach (var validationResult in vResults)
                {
                    yield return validationResult;
                }
            }

            yield break;
        }


    }
}
于 2015-11-20T23:25:23.590 に答える