【问题标题】:Is it possible to override the required attribute on a property in a model?是否可以覆盖模型中属性的必需属性?
【发布时间】:2012-02-12 19:13:02
【问题描述】:

我很想知道是否可以覆盖已在模型上设置的 [Required] 属性。我敢肯定这个问题有一个简单的解决方案,任何接受者?

【问题讨论】:

  • 定义“覆盖”。您的意思是在不需要该属性的情况下创建模型的子类,还是您的意思是让特定的控制器操作不在乎是否未提供必填字段?您是担心客户端验证还是只担心服务器端?

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


【解决方案1】:

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

【讨论】:

  • 如果其他人依赖于父类字段,你可能想从base设置和获取:get { return base.RequiredProperty; } set{ value = base.RequiredProperty;}
  • 奇怪,但这对我不起作用。我有一个属性 [Range(0,999999)] 并且子类有 [Range(0,2000)] 但验证选择父类而不是子类
  • 这不起作用public class BaseModel { [Required] public string RequiredProperty { get; set; } } public class DerivativeModel : BaseModel { new public string RequiredProperty { get; set; } }
  • 我试过这个并且 [Required] 属性也应用于继承的属性。我相信原始问题的重点是如何在继承的对象中覆盖该属性。
【解决方案2】:

@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; }
}

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

【讨论】:

  • 谢谢你写了关于ModelState验证错误的文章,我以为我做错了什么
  • 谢谢!这非常适合我需要类在一个视图模型中进行验证并在另一个视图模型中进行只读数据的情况。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      我尝试了 Mahmoud 的答案,但如果没有一些更改,它对我不起作用。将此添加为答案,以便我可以提供代码以防万一它对其他人有所帮助,但完全归功于 Mahmoud Hboubati - 我已赞成您的答案。

      在我的情况下,我有一个带有 DbGeography 属性的基本 DTO 类,这是 MVC 项目所需的,该项目使用自定义 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));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多