モデルに設定された [Required] 属性をオーバーライドできるかどうか知りたいです。この問題には簡単な解決策があると確信しています。
5 に答える
正確に何をしているかによって異なります。Required 属性を持つモデルをベースとして使用して、サブクラスを操作している場合は、次のようにすることができます。
プロパティをnew
オーバーライドするのではなく、キーワードで再定義してください。
public class BaseModel
{
[Required]
public string RequiredProperty { get; set; }
}
public class DerivativeModel : BaseModel
{
new public string RequiredProperty { get; set; }
}
モデルをバインドまたは検証するだけで、コントローラーの Required プロパティをスキップする場合は、次のようにすることができます。
public ActionResult SomeAction()
{
var model = new BaseModel();
if (TryUpdateModel(model, null, null, new[] { "RequiredProperty" })) // fourth parameter is an array of properties (by name) that are excluded
{
// updated and validated correctly!
return View(model);
}
// failed validation
return View(model);
}
カスタム検証属性を使用できます (RequiredAttribute から派生している可能性があります)。
public class RequiredExAttribute : RequiredAttribute
{
public bool UseRequiredAttribute { get; protected set; }
public RequiredExAttribute(bool IsRequired)
{
UseRequiredAttribute = IsRequired;
}
public override bool IsValid(object value)
{
if (UseRequiredAttribute)
return base.IsValid(value);
else
{
return true;
}
}
public override bool RequiresValidationContext
{
get
{
return UseRequiredAttribute;
}
}
}
public class RequiredExAttributeAdapter : RequiredAttributeAdapter
{
public RequiredExAttributeAdapter(ModelMetadata metadata, ControllerContext context, RequiredExAttribute attribute)
: base(metadata, context, attribute) { }
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
if (((RequiredExAttribute)Attribute).UseRequiredAttribute)// required -> return normal required rules
return base.GetClientValidationRules();
else// not required -> return empty rules list
return new List<ModelClientValidationRule>();
}
}
次に、Application_Start
このコード行を使用して登録します。
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredExAttribute), typeof(RequiredExAttributeAdapter));
はい、MetadataTypeクラスを使用して可能です。
[MetadataType(typeof(Base.Metadata))]
public class Base
{
public string RequiredProperty { get; set; }
public class Metadata
{
[Required]
public string RequiredProperty { get; set; }
}
}
[MetadataType(typeof(Derived.Metadata))]
public class Derived : Base
{
public new class Metadata
{
}
}
そしてそれをテストします:
var type = typeof(Derived);
var metadataType = typeof(Derived.Metadata);
var provider = new AssociatedMetadataTypeTypeDescriptionProvider(type, metadataType);
TypeDescriptor.AddProviderTransparent(provider, type);
var instance = new Derived();
var results = new List<ValidationResult>();
Validator.TryValidateObject(instance,
new ValidationContext(instance),
results,
true);
Debug.Assert(results.Count == 0);
マフムードの答えを試しましたが、いくつか変更しないとうまくいきませんでした。これを回答として追加して、他の誰かに役立つ場合に備えてコードを提供できるようにしますが、Mahmoud Hboubati に完全なクレジットを与えます-私はあなたの回答に賛成しました。
私の状況では、DbGeography タイプのカスタム EditorTemplate と DisplayTemplate を使用する MVC プロジェクトに必要な DbGeography プロパティを持つ基本 DTO クラスがありました。しかし、モデルを Web API コントローラーに投稿するために、代わりにその DTO のサブクラスに緯度/経度フィールドを追加したいと考えました。これは、DbGeography クラスのインスタンスを作成および設定して、DbGeography プロパティの値を設定するために使用されます。問題は、サブクラスのみで DbGeography プロパティを不要にすることができなかったことです。
マフムードのアプローチを使用してブール値がコンストラクターに渡されたとき、デフォルト値をオーバーライドすることはありませんでした。これは、Web API を使用しており、以下のようにファクトリ アプローチを使用して属性を登録している (Global.asax.cs Application_Start メソッド内) ためである可能性があります。
DataAnnotationsModelValidationFactory factory = (p, a) => new DataAnnotationsModelValidator(
new List<ModelValidatorProvider>(), new RequiredExAttribute()
);
DataAnnotationsModelValidatorProvider provider = new DataAnnotationsModelValidatorProvider();
provider.RegisterAdapterFactory(typeof(RequiredExAttribute), factory);
属性クラスを次のように変更する必要がありました。
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
...
public class RequiredExAttribute : RequiredAttribute
{
public bool IsRequired { get; set; }
public override bool IsValid(object value)
{
if (IsRequired)
return base.IsValid(value);
else
{
return true;
}
}
public override bool RequiresValidationContext
{
get
{
return IsRequired;
}
}
}
public class RequiredExAttributeAdapter : RequiredAttributeAdapter
{
public RequiredExAttributeAdapter(ModelMetadata metadata, ControllerContext context, RequiredExAttribute attribute)
: base(metadata, context, attribute) { }
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
if (((RequiredExAttribute)Attribute).IsRequired)// required -> return normal required rules
return base.GetClientValidationRules();
else// not required -> return empty rules list
return new List<ModelClientValidationRule>();
}
}
基本クラス:
[RequiredEx(IsRequired = true)]
public virtual DbGeography Location { get; set; }
サブクラス:
[RequiredEx(IsRequired = false)]
public override DbGeography Location { get; set; }
[Required]
public decimal Latitude { get; set; }
[Required]
public decimal Longitude { get; set; }
MVCプロジェクトに属性を登録するためにMahmoudが上記で行ったのと同じ方法を使用したことに注意してください。
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredExAttribute), typeof(RequiredExAttributeAdapter));