DataAnnotations“NotRequired”属性(DataAnnotations “No

2019-07-29 12:26发布

我有一个模型,一种复杂的。

我有我的UserViewModel其中有几个属性,其中两个是HomePhoneWorkPhone 。 这两种类型的PhoneViewModel 。 在PhoneViewModel我有CountryCodeAreaCodeNumber的所有字符串。 我想使CountryCode可选的,但AreaCodeNumber是强制性的。

这个伟大的工程。 我的问题是,在UserViewModel WorkPhone是强制性的, HomePhone不是。

反正我有可以dissable Require在attributs PhoneViewModel通过设置任何属性HomeWork财产?

我已经试过这样:

[ValidateInput(false)]

但它仅适用于类和方法。

码:

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

Answer 1:

[更新2012/5/24使想法更加清晰]

我不知道这是正确的做法,但我认为你可以扩展这一概念,并能创造一个更通用的/可重复使用的方法。

在ASP.NET MVC的验证发生在结合阶段。 当你提交表单到服务器的DefaultModelBinder是从请求信息来创建模型实例,并添加验证错误的一个ModelStateDictionary

在你的情况,只要绑定与发生HomePhone的验证会火起来, 我认为我们不能创建自定义验证属性或相似类型做很多这一点。

所有我想是不是在所有创建模型实例HomePhone属性时,有没有在书(AREACODE,COUNTRYCODE和数量或空)可用值,当我们控制结合我们控制了验证,对于这一点,我们要创建一个自定义模型粘合剂

自定义模型绑定我们正在检查,如果该属性是HomePhone如果表单包含任何值的属性,如果没有,我们没有在属性绑定和验证将不会发生HomePhone 。 简单地说,价值HomePhone将在空UserViewModel

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

最后,你必须注册在Global.asax.cs中的自定义模型粘合剂。

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

所以,现在的你有需要UserViewModel作为参数的动作,

 [HttpPost]
 public Action Post(UserViewModel userViewModel)
 {

 }

我们的自定义模型粘合剂发挥作用,形式不张贴的AREACODE,COUNTRYCODE和号码的任何值HomePhone ,不会有任何验证错误和userViewModel.HomePhone为空。 如果表单发布ATLEAST这些属性值中的任何一个,则验证会发生的HomePhone预期。



Answer 2:

我一直在使用这个惊人的NuGet,做动态注释: ExpressiveAnnotations

它可以让你做的事情,是不可能的,如前

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

甚至

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

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

正如@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);
        }


    }
}


Answer 3:

我不会与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; }
}

然后:

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

如果它是不明确的,我会提供一个解释,但我认为这是不言自明。



Answer 4:

只是一些观察:下面的代码淡然一个问题,如果绑定是不是简单的申请了。 我必须在对象嵌套对象时,它会跳过它,caouse有些提起没有在嵌套的对象绑定的情况下。

可能的解决方法是

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

很多感谢阿尔塔夫卡特里



文章来源: DataAnnotations “NotRequired” attribute