是否可以覆盖模型中属性的必需属性?

时间:2012-01-18 00:55:35

标签: c# asp.net asp.net-mvc asp.net-mvc-3 modelmetadata

我很想知道是否可以覆盖模型上设置的[Required]属性。我相信这个问题最简单的解决方案是什么,任何参与者?

5 个答案:

答案 0 :(得分:18)

取决于你究竟在做什么。如果您正在使用子类,使用具有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);
}

答案 1 :(得分:13)

@HackedByChinese方法很好,但它包含一个问题

public class BaseModel
{
    [Required]
    public string RequiredProperty { get; set; }
}

public class DerivativeModel : BaseModel
{
    new public string RequiredProperty { get; set; }
}

此代码在ModelState中为您提供验证错误,如果您在表单上使用DerivativeModeloverride也无效,那么您无法删除Required通过覆盖或更新它的属性,所以我找到了某种解决方法

public class BaseModel
{
    public virtual string RequiredProperty { get; set; }
}

public class DerivativeModel : BaseModel
{
    [Required]
    public override string RequiredProperty { get; set; }
}

public class DerivativeModel2 : BaseModel
{
    [Range(1, 10)]
    public override string RequiredProperty { get; set; }
}

我有一个没有验证属性和派生类的基础模型

答案 2 :(得分:5)

您可以使用自定义验证属性(可能来自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));

答案 3 :(得分:1)

我尝试了Mahmoud的答案,但如果没有一些改动,它对我没有用。添加这个作为答案,所以我可以给出代码,以防万一它帮助其他人,但完全归功于Mahmoud Hboubati - 我已经赞成你的答案。

在我的情况下,我有一个基础DTO类,它具有一个MVC项目所需的DbGeography属性,该项目使用自定义EditorTemplate和DisplayTemplate作为DbGeography类型。但是,为了将模型发布到Web API控制器,我希望将纬度/经度字段添加到该DTO的子类中,这将用于创建和设置DbGeography类的实例以在DbGeography属性上设置值。问题是,我无法仅在子类上使用DbGeography属性。

当使用Mahmoud的方法在构造函数中传递布尔值时,它似乎永远不会覆盖我的默认值。这可能是因为我正在使用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; }

注意,我使用与上面Mahmoud相同的方法在我的MVC项目中注册属性:

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredExAttribute), typeof(RequiredExAttributeAdapter));

答案 4 :(得分:1)

是的,可以使用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);
相关问题