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

发布于 2024-12-27 16:36:58 字数 67 浏览 1 评论 0原文

我很好奇是否可以覆盖模型上设置的 [Required] 属性。我确信这个问题大多数都会有一个简单的解决方案,有人接受吗?

I'm curious to find out if it is possible to override the [Required] attribute that has been set on a model. I'm sure there most be a simple solution to this problem, any takers?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

亽野灬性zι浪 2025-01-03 16:36:58

取决于你具体在做什么。如果您正在使用子类,并使用具有必需属性的模型作为基础,则可以执行以下操作:

使用 new 关键字重新定义属性,而不是覆盖它。

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


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

}

如果您只是想绑定或验证模型,但跳过控制器中的必需属性,您可以执行以下操作:

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

Depends on what precisely you are doing. If you are working with a subclass, using the model with the Required attribute as the base, you can do this:

Redefine the property with the new keyword, rather than override it.

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


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

}

If you simply want to bind or validate a model, but skip the Required property in your controller, you can do something like:

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);
}
面如桃花 2025-01-03 16:36:58

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

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

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

即使您在表单上使用 DerivativeModel,此代码也会在 ModelState 中给出验证错误,override 也不起作用,所以你不能通过覆盖或更新它来删除 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; }
}

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

@HackedByChinese method is fine, but it contains a problem

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

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

This code give you a validation error in ModelState EVEN if you use DerivativeModel on the form, override doesn't work either, so you cannot delete Required attribute by overriding or renewin it, so I came to a some sort of a workaround

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

I have a base model with no validation attributes and derived classes

蔚蓝源自深海 2025-01-03 16:36:58

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

You can use a custom validation attribute (it might be derived from 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>();
        }
    }

and then regester it in Application_Start using this code line:

 DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredExAttribute), typeof(RequiredExAttributeAdapter));
泼猴你往哪里跑 2025-01-03 16:36:58

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

Yes, it is possible using the MetadataType class, e.g:

[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
    {
    }
}

And test it:

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);
仅冇旳回忆 2025-01-03 16:36:58

我尝试了马哈茂德的答案,但如果没有一些改变,它对我不起作用。添加这个作为答案,这样我就可以给出代码,以防它对其他人有帮助,但完全归功于 Mahmoud Hboubati - 我已经对你的答案投了赞成票。

在我的情况下,我有一个带有 DbGeography 属性的基本 DTO 类,该属性是 MVC 项目所需的,该项目使用 DbGeography 类型的自定义 EditorTemplate 和 DisplayTemplate。但为了将模型发布到 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>();
    }
}

Base Class:

[RequiredEx(IsRequired = true)]
public virtual DbGeography Location { get; set; }

Subclass:

[RequiredEx(IsRequired = false)]
public override DbGeography Location { get; set; }

[Required]
public decimal Latitude { get; set; }

[Required]
public decimal Longitude { get; set; }

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

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

I tried Mahmoud's answer, but it didn't work for me without a few changes. Adding this as an answer so I can give the code that did in case it helps someone else, but full credit to Mahmoud Hboubati - I've upvoted your answer.

In my situation I had a base DTO class with a DbGeography property that was required for an MVC project which used a custom EditorTemplate and DisplayTemplate for the DbGeography type. But for posting a model to a Web API controller I wanted to have latitude/longitude fields added to a subclass of that DTO instead, which would be used to create and set an instance of a DbGeography class to set the value on the DbGeography property. Problem was, I couldn't make the DbGeography property not required on the subclass only.

When the boolean value was passed in the constructor using Mahmoud's approach, it never seemed to override the default value for me. That might be because I'm using Web API and registering the attribute using the factory approach, like below (in Global.asax.cs Application_Start method):

DataAnnotationsModelValidationFactory factory = (p, a) => new DataAnnotationsModelValidator(
    new List<ModelValidatorProvider>(), new RequiredExAttribute()
);

DataAnnotationsModelValidatorProvider provider = new DataAnnotationsModelValidatorProvider();
provider.RegisterAdapterFactory(typeof(RequiredExAttribute), factory);

I had to change the attribute class to this:

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>();
    }
}

Base Class:

[RequiredEx(IsRequired = true)]
public virtual DbGeography Location { get; set; }

Subclass:

[RequiredEx(IsRequired = false)]
public override DbGeography Location { get; set; }

[Required]
public decimal Latitude { get; set; }

[Required]
public decimal Longitude { get; set; }

Note, I used same method as Mahmoud did above for registering the attribute in my MVC project:

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredExAttribute), typeof(RequiredExAttributeAdapter));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文