ASP.NET MVC条件付き検証


129

データ注釈を使用してモデルの条件付き検証を行う方法は?

たとえば、次のモデル(PersonとSenior)があるとします。

public class Person
{
    [Required(ErrorMessage = "*")]
    public string Name
    {
        get;
        set;
    }

    public bool IsSenior
    {
        get;
        set;
    }

    public Senior Senior
    {
        get;
        set;
    }
}

public class Senior
{
    [Required(ErrorMessage = "*")]//this should be conditional validation, based on the "IsSenior" value
    public string Description
    {
        get;
        set;
    }
}

そして次のビュー:

<%= Html.EditorFor(m => m.Name)%>
<%= Html.ValidationMessageFor(m => m.Name)%>

<%= Html.CheckBoxFor(m => m.IsSenior)%>
<%= Html.ValidationMessageFor(m => m.IsSenior)%>

<%= Html.CheckBoxFor(m => m.Senior.Description)%>
<%= Html.ValidationMessageFor(m => m.Senior.Description)%>

「IsSenior」プロパティの選択に基づいて、「Senior.Description」プロパティの条件付き必須フィールドになりたい(true->必須)。データアノテーションを使用してASP.NET MVC 2に条件付き検証を実装する方法


1
私は最近、同様の質問をしてきました:stackoverflow.com/questions/2280539/...
ダーリン・ディミトロフ

よくわかりません。Seniorオブジェクトは常に先輩ですので、なぜIsSeniorは、その場合の偽することができます。Person.IsSeniorがfalseの場合、「Person.Senior」プロパティをnullにする必要があるだけではありませんか。または、IsSenior次のようにプロパティを実装しないのはなぜですかbool IsSenior { get { return this.Senior != null; } }
Steven

Steven:「IsSenior」は、ビューのチェックボックスフィールドに変換されます。ユーザーが「IsSenior」チェックボックスをオンにすると、「Senior.Description」フィールドが必須になります。
Peter Stegnar 2010年

ダリン・ディミトロフ:まあまあですが、完全ではありません。エラーメッセージが特定のフィールドに関連していることをどのように達成しますか?オブジェクトレベルで検証すると、オブジェクトレベルでエラーが発生します。プロパティレベルでエラーが必要です。
Peter Stegnar 2010年

回答:


150

MVC3に条件付き検証ルールを追加するより良い方法があります。モデルIValidatableObjectValidateメソッドを継承および実装させます。

public class Person : IValidatableObject
{
    public string Name { get; set; }
    public bool IsSenior { get; set; }
    public Senior Senior { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
    { 
        if (IsSenior && string.IsNullOrEmpty(Senior.Description)) 
            yield return new ValidationResult("Description must be supplied.");
    }
}

ASP.NET MVC 3の紹介(プレビュー1)で詳細をご覧ください。


プロパティは、フィールド、検証がない塗りつぶしという場合でない仕事...、値を必要とする「INT」タイプの場合
Jeyhun Rahimovは、

2
残念ながら、マイクロソフトはこれを間違ったレイヤーに入れました-検証はビジネスロジックであり、このインターフェイスはSystem.Web DLLにあります。これを使用するには、ビジネスレイヤーにプレゼンテーションテクノロジーへの依存関係を与える必要があります。
NightOwl888 2013

7
あなたがそれを実装すればあなたはそうする-falconwebtech.com/post/…で
viperguynaz

4
falconwebtech.com/post/…-@viperguynazこれは機能していません
Smit Patel

1
@RayLoveless呼び出す必要があります-ValidateをModelState.IsValid直接呼び出さないでください
viperguynaz

63

これは、コントローラーに含まれている「ModelState」辞書を処理することで解決しました。ModelState辞書には、検証が必要なすべてのメンバーが含まれています。

これが解決策です:

一部のフィールドに基づいて条件付き検証を実装する必要がある場合(たとえば、A = trueの場合はBが必要)、プロパティレベルのエラーメッセージングを維持しながら(これはオブジェクトレベルのカスタムバリデーターには当てはまりません)、これを実現できます。 「ModelState」を処理することにより、不要な検証を単純に削除します。

...あるクラスで...

public bool PropertyThatRequiredAnotherFieldToBeFilled
{
  get;
  set;
}

[Required(ErrorMessage = "*")] 
public string DepentedProperty
{
  get;
  set;
}

...クラスは続きます...

...一部のコントローラアクションでは...

if (!PropertyThatRequiredAnotherFieldToBeFilled)
{
   this.ModelState.Remove("DepentedProperty");
}

...

これにより、他のすべてを同じままにしながら、条件付き検証を実現します。


更新:

これが私の最後の実装です。モデルのインターフェースと、そのインターフェースを実装するモデルを検証するaction属性を使用しました。インターフェースはValidate(ModelStateDictionary modelState)メソッドを規定します。アクションの属性は、IValidatorSomethingのValidate(modelState)を呼び出すだけです。

私はこの回答を複雑にしたくなかったので、最終的な実装の詳細については触れませんでした(最終的には量産コードで問題になります)。


17
欠点は、検証ロジックがモデルに配置されている部分とコントローラーに配置されている部分です。
Kristof Claes

もちろん、これは必要ありません。最も基本的な例を示します。これを、モデルのインターフェイスと、言及されたインターフェイスを実装するモデルを検証するアクション属性で実装しました。インターフェースはValidate(ModelStateDictionary modelState)メソッドを発汗させます。最後に、モデルですべての検証を行います。とにかく、良い点。
Peter Stegnar、2010年

私は、MVCチームがすぐに何かを構築するまで、このアプローチの単純さを気に入っています。しかし、あなたのソリューションはクライアント側の検証を有効にして動作しますか?
アーロン、

2
@Aaron:ソリューションが気に入ってうれしいですが、残念ながらこのソリューションはクライアント側の検証では機能しません(すべての検証属性にはJavaScript実装が必要なため)。「Remote」属性で自分自身を助けることができるので、それを検証するためにAjax呼び出しが発行されるだけです。
Peter Stegnar、2011

この答えをさらに詳しく説明できますか?これは理にかなっていますが、私はその上にあることを確認したいと思います。私はこの正確な状況に直面しており、それを解決したいと考えています。
Richard B

36

昨日も同じ問題がありましたが、クライアント側とサーバー側の両方の検証で機能する非常にクリーンな方法でそれを行いました。

条件:モデル内の他のプロパティの値に基づいて、別のプロパティを必須にする場合。これがコードです

public class RequiredIfAttribute : RequiredAttribute
{
    private String PropertyName { get; set; }
    private Object DesiredValue { get; set; }

    public RequiredIfAttribute(String propertyName, Object desiredvalue)
    {
        PropertyName = propertyName;
        DesiredValue = desiredvalue;
    }

    protected override ValidationResult IsValid(object value, ValidationContext context)
    {
        Object instance = context.ObjectInstance;
        Type type = instance.GetType();
        Object proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
        if (proprtyvalue.ToString() == DesiredValue.ToString())
        {
            ValidationResult result = base.IsValid(value, context);
            return result;
        }
        return ValidationResult.Success;
    }
}

ここで、PropertyNameは、条件を作成するプロパティです。DesiredValueは、必要に応じて他のプロパティを検証する必要があるPropertyName(プロパティ)の特定の値です。

あなたが次のものを持っているとしましょう

public class User
{
    public UserType UserType { get; set; }

    [RequiredIf("UserType", UserType.Admin, ErrorMessageResourceName = "PasswordRequired", ErrorMessageResourceType = typeof(ResourceString))]
    public string Password
    {
        get;
        set;
    }
}

最後に、少なくともクライアント側の検証を実行できるように、属性のアダプターを登録します(私はglobal.asax、Application_Startに配置します)。

 DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute),typeof(RequiredAttributeAdapter));

これはされた元の開始点であるmiroprocessordev.blogspot.com/2012/08/...
ダン・ヒューネックス

asp.net mvc2に同等の解決策はありますか?ValidationResult、ValidationContextクラスは、asp.net mvc2(.net framework 3.5)では使用できません
User_MVC

2
これは、リンクされたブログが述べているように、サーバー側でのみ機能します
パックマン、2014年

2
私はMVC5を使用してクライアント側でこれを機能させることができましたが、クライアントでは、DesiredValueが何であっても検証を起動します。
Geethanga 2014

1
@Dan Hunex:MVC4では、クライアント側で適切に機能することができず、DesiredValueが何であっても検証が実行されます。何か助けpls?
Jack

34

私は動的注釈を行うこの驚くべきナゲットを使用していますExpressiveAnnotations

あなたが夢見ることができるロジックを検証することができます:

public string Email { get; set; }
public string Phone { get; set; }
[RequiredIf("Email != null")]
[RequiredIf("Phone != null")]
[AssertThat("AgreeToContact == true")]
public bool? AgreeToContact { get; set; }

3
ExpressiveAnnotationライブラリは、ここでのすべての回答の最も柔軟で汎用的なソリューションです。共有してくれてありがとう!
Sudhanshu Mishra 2016

2
私は堅実な一日の解決策を見つけるために頭を叩いてきました。ExpressiveAnnotationsは私にとって修正のようです!
キャバーマン2017年

ExpressiveAnnotationライブラリは素晴らしいです!
Doug Knudsen 2017

1
クライアント側のサポートもあります!
Nattras、2017

1
ただし、.NET Coreはサポートされていません。また、そうなるとは思われません。
gosr 2017

18

ModelStateからエラーを削除して、条件付きでバリデーターを無効にすることができます。

ModelState["DependentProperty"].Errors.Clear();


6

現在、この条件付き検証を行う(他の便利なデータアノテーション検証の中でも特に)フレームワークが用意されています。http//foolproof.codeplex.com/

具体的には、[RequiredIfTrue( "IsSenior")]バリデーターを見てください。これを検証するプロパティに直接配置すると、「Senior」プロパティに関連付けられている検証エラーの望ましい動作が得られます。

NuGetパッケージとして入手できます。


3

シニアレベルではなく、個人レベルで検証する必要があります。そうでない場合、シニアは親の個人への参照を持っている必要があります。個人の検証ではなく、そのプロパティの1つを定義する自己検証メカニズムが必要なようです。確かではありませんが、DataAnnotationsがそのままではこれをサポートするとは思いません。あなた自身を作成する何ができるAttributeから派生というValidationAttributeことは、クラスレベルで装飾され、次のものもクラスレベルのバリデータを実行することを可能にすることをカスタムバリデータを作成することができます。

Validation Application Blockはそのままの状態で自己検証をサポートしますが、VABはかなり急な学習曲線を持っています。それでも、VABを使用した例を次に示します。

[HasSelfValidation]
public class Person
{
    public string Name { get; set; }
    public bool IsSenior { get; set; }
    public Senior Senior { get; set; }

    [SelfValidation]
    public void ValidateRange(ValidationResults results)
    {
        if (this.IsSenior && this.Senior != null && 
            string.IsNullOrEmpty(this.Senior.Description))
        {
            results.AddResult(new ValidationResult(
                "A senior description is required", 
                this, "", "", null));
        }
    }
}

「シニアレベルではなく、個人レベルで検証する必要があります」はいこれはオプションですが、シニアオブジェクトで必要な特定のフィールドにエラーが追加される機能を失います。
Peter Stegnar

3

同じ問題があり、[必須]属性の変更が必要でした-httpリクエストに応じてフィールドを必須にしてください。解決策はDan Hunexの回答に似ていましたが、彼の解決策は正しく機能しませんでした(コメントを参照)。私は目立たない検証を使用せず、MicrosoftMvcValidation.jsをそのまま使用します。ここにあります。カスタム属性を実装します。

public class RequiredIfAttribute : RequiredAttribute
{

    public RequiredIfAttribute(/*You can put here pararmeters if You need, as seen in other answers of this topic*/)
    {

    }

    protected override ValidationResult IsValid(object value, ValidationContext context)
    {

    //You can put your logic here   

        return ValidationResult.Success;//I don't need its server-side so it always valid on server but you can do what you need
    }


}

次に、カスタムプロバイダーを実装して、global.asaxのアダプターとして使用する必要があります。

public class RequreIfValidator : DataAnnotationsModelValidator <RequiredIfAttribute>
{

    ControllerContext ccontext;
    public RequreIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute)
       : base(metadata, context, attribute)
    {
        ccontext = context;// I need only http request
    }

//override it for custom client-side validation 
     public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
     {       
               //here you can customize it as you want
         ModelClientValidationRule rule = new ModelClientValidationRule()
         {
             ErrorMessage = ErrorMessage,
    //and here is what i need on client side - if you want to make field required on client side just make ValidationType "required"    
             ValidationType =(ccontext.HttpContext.Request["extOperation"] == "2") ? "required" : "none";
         };
         return new ModelClientValidationRule[] { rule };
      }
}

そして、あなたのglobal.asaxを行で変更してください

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequreIfValidator));

そしてここにあります

[RequiredIf]
public string NomenclatureId { get; set; }

私にとっての主な利点は、控えめな検証の場合のように、カスタムクライアントバリデーターをコーディングする必要がないことです。[必須]と同じように機能しますが、必要な場合にのみ機能します。


拡張に関する部分DataAnnotationsModelValidatorは、まさに私が見る必要があったものでした。ありがとうございました。
2016年


0

モデル状態からのエラーの条件付き削除の一般的な使用法:

  1. コントローラーアクションの条件付きの最初の部分にする
  2. ModelStateからエラーを削除するロジックを実行します
  3. 既存のロジックの残りの部分を実行します(通常、モデルの状態の検証、その他すべて)

例:

public ActionResult MyAction(MyViewModel vm)
{
    // perform conditional test
    // if true, then remove from ModelState (e.g. ModelState.Remove("MyKey")

    // Do typical model state validation, inside following if:
    //     if (!ModelState.IsValid)

    // Do rest of logic (e.g. fetching, saving

あなたの例では、すべてをそのままにして、コントローラのアクションに提案されたロジックを追加します。コントローラーアクションに渡されたViewModelには、UIからデータが入力されたPersonおよびSenior Personオブジェクトがあると想定しています。


0

私はMVC 5を使用していますが、次のようなことを試すことができます。

public DateTime JobStart { get; set; }

[AssertThat("StartDate >= JobStart", ErrorMessage = "Time Manager may not begin before job start date")]
[DisplayName("Start Date")]
[Required]
public DateTime? StartDate { get; set; }

あなたの場合、「IsSenior == true」のようなものを言うでしょう。次に、ポストアクションの検証を確認するだけです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.