データ注釈を使用してモデルの条件付き検証を行う方法
たとえば、次のモデル(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に条件付き検証を実装する方法は?
コントローラに含まれる "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)を呼び出すだけです。
私はこの答えを複雑にしたくなかったので、最終的な実装の詳細について言及しませんでした(最終的には本番コードで問題になります)。
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 をご覧ください
私は昨日同じ問題を抱えていましたが、クライアント側とサーバー側の両方の検証で機能する非常にきれいな方法でそれを行いました。
条件:モデル内の他のプロパティの値に基づいて、別のプロパティを必須にする必要があります。ここにコードがあります
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));
私は動的な注釈を行うこの驚くべきナゲットを使用してきました 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; }
ModelStateからエラーを削除することにより、バリデーターを条件付きで無効にできます。
ModelState["DependentProperty"].Errors.Clear();
ありがとうメリット:)
誰かが便利だと思った場合に備えて、これをMVC 3に更新しました。 http://blogs.msdn.com/b/simonince/archive/2011/02/04/conditional-validation-in-asp-net-mvc-3.aspx
サイモン
現在、この条件付き検証(他の便利なデータ注釈検証)をすぐに実行できるフレームワークがあります。 http://foolproof.codeplex.com/
具体的には、[RequiredIfTrue( "IsSenior")]バリデーターを見てください。これを検証するプロパティに直接配置するため、「Senior」プロパティに関連付けられた検証エラーの望ましい動作が得られます。
NuGetパッケージとして入手できます。
私は同じ問題を抱えていて、[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; }
私にとっての主な利点は、控えめな検証の場合のようにカスタムクライアントバリデータをコーディングする必要がないことです。 [必須]と同じように機能しますが、必要な場合にのみ機能します。
シニアレベルではなく個人レベルで検証する必要があります。または、シニアは親の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));
}
}
}
この男をチェックしてください:
http://blogs.msdn.com/b/simonince/archive/2010/06/04/conditional-validation-in-mvc.aspx
私は今、彼のサンプルプロジェクトを進めています。
モデル状態からのエラーの条件付き削除の一般的な使用法:
例:
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オブジェクトがあると仮定しています。
私は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」のようなことを言うでしょう。次に、投稿アクションの検証を確認する必要があります。