web-dev-qa-db-ja.com

ASP.NET MVC条件付き検証

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

たとえば、次のモデル(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-> required)。データ注釈付きのASP.NET MVC 2に条件付き検証を実装する方法は?

121
Peter Stegnar

コントローラに含まれる "ModelState" 辞書を処理することでこれを解決しました。 ModelStateディクショナリには、検証する必要があるすべてのメンバーが含まれます。

解決策は次のとおりです。

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

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

public bool PropertyThatRequiredAnotherFieldToBeFilled
{
  get;
  set;
}

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

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

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

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

...

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


UPDATE:

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

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

61
Peter Stegnar

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

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.");
    }
}

詳細は http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx をご覧ください

143
viperguynaz

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

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

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));
33
Dan Hunex

私は動的な注釈を行うこの驚くべきナゲットを使用してきました 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; }
28
Korayem

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

ModelState["DependentProperty"].Errors.Clear();
17
Pavel Chuchuva

ありがとうメリット:)

誰かが便利だと思った場合に備えて、これをMVC 3に更新しました。 http://blogs.msdn.com/b/simonince/archive/2011/02/04/conditional-validation-in-asp-net-mvc-3.aspx

サイモン

8
Simon Ince

現在、この条件付き検証(他の便利なデータ注釈検証)をすぐに実行できるフレームワークがあります。 http://foolproof.codeplex.com/

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

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

6
bojingo

私は同じ問題を抱えていて、[Required]属性の変更が必要でした-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; }

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

3
Den

シニアレベルではなく個人レベルで検証する必要があります。または、シニアは親のPersonへの参照を持っている必要があります。私は、そのプロパティの1つではなく、Personの検証を定義する自己検証メカニズムが必要なようです。よくわかりませんが、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));
        }
    }
}
3
Steven

この男をチェックしてください:

http://blogs.msdn.com/b/simonince/archive/2010/06/04/conditional-validation-in-mvc.aspx

私は今、彼のサンプルプロジェクトを進めています。

2
Merritt

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

  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」のようなことを言うでしょう。次に、投稿アクションの検証を確認する必要があります。

0
fosterImposter