12

我有一个复杂的模型。

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

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

无论如何我可以通过在Require属性中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; }
}
4

4 回答 4

5

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

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

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

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

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

自定义模型绑定器中,我们正在检查属性是否存在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 和 number发布任何值,HomePhone不会出现任何验证错误并且userViewModel.HomePhone为 null。如果表单至少发布了这些属性的任何一个值,那么验证将按HomePhone预期进行。

于 2012-05-23T17:28:01.473 回答
3

我一直在使用这个惊人的 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);
        }


    }
}
于 2016-04-20T12:43:30.590 回答
2

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

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

于 2012-05-24T09:17:52.763 回答
1

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

可能的解决方案是

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

非常感谢Altaf Khatri

于 2013-08-26T20:13:31.797 回答