C# DataAnnotations“NotRequired”属性

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10722428/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 14:59:44  来源:igfitidea点击:

DataAnnotations "NotRequired" attribute

c#asp.net-mvcasp.net-mvc-3data-annotationsvalidationattribute

提问by Diego

I've a model kind of complicated.

我有一个模型有点复杂。

I have my UserViewModelwhich has several properties and two of them are HomePhoneand WorkPhone. Both of type PhoneViewModel. In PhoneViewModelI have CountryCode, AreaCodeand Numberall strings. I want to make the CountryCodeoptional but AreaCodeand Numbermandatory.

我有 my UserViewModelwhich 有几个属性,其中两个是HomePhoneand WorkPhone。两种类型PhoneViewModel。在PhoneViewModel我有CountryCode,AreaCodeNumber所有字符串。我想使CountryCode可选但AreaCodeNumber强制性。

This works great. My problem is that in the UserViewModelWorkPhoneis mandatory, and HomePhoneis not.

这很好用。我的问题是 inUserViewModelWorkPhone是强制性的,而HomePhone不是。

Is there anyway I can dissable Requireattributs in PhoneViewModelby setting any attributes in HomeWorkproperty?

无论如何我可以通过在Require属性中PhoneViewModel设置任何属性来禁用属性HomeWork吗?

I've tried this:

我试过这个:

[ValidateInput(false)]

but it is only for classes and methods.

但它仅适用于类和方法。

Code:

代码:

public class UserViewModel
{
    [Required]
    public string Name { get; set; }

    public PhoneViewModel HomePhone { get; set; }

    [Required]    
    public PhoneViewModel WorkPhone { get; set; }
}

public class PhoneViewModel
{
    public string CountryCode { get; set; }

    public string AreaCode { get; set; }

    [Required]
    public string Number { get; set; }
}

采纳答案by VJAI

[UPDATED on 5/24/2012 to make the idea more clear]

[更新于 2012 年 5 月 24 日,使想法更清晰]

I'm not sure this is the right approach but I think you can extend the concept and can create a more generic / reusable approach.

我不确定这是正确的方法,但我认为您可以扩展这个概念并创建更通用/可重用的方法。

In ASP.NET MVC the validation happens at the binding stage. When you are posting a form to the server the DefaultModelBinderis the one that creates model instances from the request information and add the validation errors to the ModelStateDictionary.

在 ASP.NET MVC 中,验证发生在绑定阶段。当您将表单发布到服务器时,该表单DefaultModelBinder会根据请求信息创建模型实例并将验证错误添加到ModelStateDictionary.

In your case, as long as the binding happens with the HomePhonethe validations will fire up and I thinkwe can't do much about this by creating custom validation attributes or similar kind.

在你的情况下,只要绑定发生,HomePhone验证就会启动,我认为我们不能通过创建自定义验证属性或类似的类型来做很多事情。

All I'm thinking is not to create model instance at all for HomePhoneproperty when there are no values available in the form (the areacode, countrycode and number or empty), when we control the binding we control the validation, for that, we have to create a custom model binder.

HomePhone当表单中没有可用值(区号、国家代码和数字或空)时,我所想的根本不是为属性创建模型实例,当我们控制绑定时,我们控制验证,为此,我们有创建自定义模型绑定器

In the custom model binderwe are checking if the property is HomePhoneand if the form contains any values for it's properties and if not we don't bind the property and the validations won't happen for HomePhone. Simply, the value of HomePhonewill be null in the UserViewModel.

自定义模型绑定器中,我们正在检查属性是否存在HomePhone以及表单是否包含其属性的任何值,如果没有,我们不绑定属性并且不会对HomePhone. 简单地说, 的值HomePhoneUserViewModel.

  public class CustomModelBinder : DefaultModelBinder
  {
      protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
      {
        if (propertyDescriptor.Name == "HomePhone")
        {
          var form = controllerContext.HttpContext.Request.Form;

          var countryCode = form["HomePhone.CountryCode"];
          var areaCode = form["HomePhone.AreaCode"];
          var number = form["HomePhone.Number"];

          if (string.IsNullOrEmpty(countryCode) && string.IsNullOrEmpty(areaCode) && string.IsNullOrEmpty(number))
            return;
        }

        base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
      }
  }

Finally you have to register the custom model binder in global.asax.cs.

最后,您必须在 global.asax.cs 中注册自定义模型绑定器。

  ModelBinders.Binders.Add(typeof(UserViewModel), new CustomModelBinder());

So now of you have an action that takes UserViewModel as parameter,

所以现在你有一个以 UserViewModel 作为参数的动作,

 [HttpPost]
 public Action Post(UserViewModel userViewModel)
 {

 }

Our custom model binder come into play and of form doesn't post any values for the areacode, countrycode and numberfor HomePhone, there won't be any validation errors and the userViewModel.HomePhoneis null. If the form posts atleast any one of the value for those properties then the validation will happen for HomePhoneas expected.

我们的自定义模型绑定器开始发挥作用,并且形式上不会发布任何区域代码、国家代码和号码的HomePhone,不会有任何验证错误并且userViewModel.HomePhone为空。如果表单至少发布了这些属性的任何一个值,则验证将按HomePhone预期进行。

回答by iappwebdev

I wouldn't go with the modelBinder; I'd use a custom ValidationAttribute:

我不会使用 modelBinder;我会使用自定义 ValidationAttribute:

public class UserViewModel
{
    [Required]
    public string Name { get; set; }

    public HomePhoneViewModel HomePhone { get; set; }

    public WorkPhoneViewModel WorkPhone { get; set; }
}

public class HomePhoneViewModel : PhoneViewModel 
{
}

public class WorkPhoneViewModel : PhoneViewModel 
{
}

public class PhoneViewModel 
{
    public string CountryCode { get; set; }

    public string AreaCode { get; set; }

    [CustomRequiredPhone]
    public string Number { get; set; }
}

And then:

进而:

[AttributeUsage(AttributeTargets.Property]
public class CustomRequiredPhone : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        ValidationResult validationResult = null;

        // Check if Model is WorkphoneViewModel, if so, activate validation
        if (validationContext.ObjectInstance.GetType() == typeof(WorkPhoneViewModel)
         && string.IsNullOrWhiteSpace((string)value) == true)
        {
            this.ErrorMessage = "Phone is required";
            validationResult = new ValidationResult(this.ErrorMessage);
        }
        else
        {
            validationResult = ValidationResult.Success;
        }

        return validationResult;
    }
}

If it is not clear, I'll provide an explanation but I think it's pretty self-explanatory.

如果不清楚,我会提供解释,但我认为这是不言自明的。

回答by lpastor

Just some observation: the following code couse a problem if the binding is more than simple filed. I you have a case that in object have nested object it going to skip it and caouse that some filed not been binded in nested object.

只是一些观察:如果绑定不仅仅是简单的字段,则以下代码会出现问题。我有一个案例,在对象中有嵌套对象,它会跳过它并导致某些文件未绑定在嵌套对象中。

Possible solution is

可能的解决方案是

protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
     {
         if (!propertyDescriptor.Attributes.OfType<RequiredAttribute>().Any())
         {
             var form = controllerContext.HttpContext.Request.Form;

             if (form.AllKeys.Where(k => k.StartsWith(string.Format(propertyDescriptor.Name, "."))).Count() > 0)
             {
                 if (form.AllKeys.Where(k => k.StartsWith(string.Format(propertyDescriptor.Name, "."))).All(
                         k => string.IsNullOrWhiteSpace(form[k])))
                     return;
             }
         }

         base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
     }

much thanks to Altaf Khatri

非常感谢Altaf Khatri

回答by Korayem

I've been using this amazing nuget that does dynamic annotations: ExpressiveAnnotations

我一直在使用这个做动态注释的神奇 nuget:ExpressiveAnnotations

It allows you to do things that weren't possible before such as

它允许您做以前不可能的事情,例如

[AssertThat("ReturnDate >= Today()")]
public DateTime? ReturnDate { get; set; }

or even

甚至

public bool GoAbroad { get; set; }
[RequiredIf("GoAbroad == true")]
public string PassportNumber { get; set; }

Update: Compile annotations in a unit test to ensure no errors exist

更新:在单元测试中编译注释以确保不存在错误

As mentioned by @diego this might be intimidating to write code in a string, but the following is what I use to Unit Test all validations looking for compilation errors.

正如@diego 所提到的,在字符串中编写代码可能令人生畏,但以下是我用来对所有验证进行单元测试以查找编译错误的内容。

namespace UnitTest
{
    public static class ExpressiveAnnotationTestHelpers
    {
        public static IEnumerable<ExpressiveAttribute> CompileExpressiveAttributes(this Type type)
        {
            var properties = type.GetProperties()
                .Where(p => Attribute.IsDefined(p, typeof(ExpressiveAttribute)));
            var attributes = new List<ExpressiveAttribute>();
            foreach (var prop in properties)
            {
                var attribs = prop.GetCustomAttributes<ExpressiveAttribute>().ToList();
                attribs.ForEach(x => x.Compile(prop.DeclaringType));
                attributes.AddRange(attribs);
            }
            return attributes;
        }
    }
    [TestClass]
    public class ExpressiveAnnotationTests
    {
        [TestMethod]
        public void CompileAnnotationsTest()
        {
            // ... or for all assemblies within current domain:
            var compiled = Assembly.Load("NamespaceOfEntitiesWithExpressiveAnnotations").GetTypes()
                .SelectMany(t => t.CompileExpressiveAttributes()).ToList();

            Console.WriteLine($"Total entities using Expressive Annotations: {compiled.Count}");

            foreach (var compileItem in compiled)
            {
                Console.WriteLine($"Expression: {compileItem.Expression}");
            }

            Assert.IsTrue(compiled.Count > 0);
        }


    }
}