ASP.NET MVC 2自定义ModelValidator的问题

我试图validation我视图中的两个文本框字段中的任何一个都提供了值。 我做了这个模型validation器:

public class RequireEitherValidator : ModelValidator { private readonly string compareProperty; private readonly string errorMessage; public RequireEitherValidator(ModelMetadata metadata, ControllerContext context, string compareProperty, string errorMessage) : base(metadata, context) { this.compareProperty = compareProperty; this.errorMessage = errorMessage; } public override IEnumerable Validate(object container) { if (Metadata.Model == null) yield break; var propertyInfo = container.GetType().GetProperty(compareProperty); if (propertyInfo == null) throw new InvalidOperationException("Unknown property:" + compareProperty); string valueToCompare = propertyInfo.GetValue(container, null).ToString(); if (string.IsNullOrEmpty(Metadata.Model.ToString()) && string.IsNullOrEmpty(valueToCompare)) yield return new ModelValidationResult { Message = errorMessage }; } } 

这个validation逻辑永远不会被击中,我认为这是因为没有值提供给文本框。

如果您需要它,这里是我创建的提供者和属性以及属性用法:

 public class MyValidatorProvider : AssociatedValidatorProvider { protected override IEnumerable GetValidators( ModelMetadata metadata, ControllerContext context, IEnumerable attributes) { foreach (var attrib in attributes.OfType()) yield return new RequireEitherValidator(metadata, context, attrib.CompareProperty, attrib.ErrorMessage); } } public class RequireEitherAttribute : Attribute { public readonly string CompareProperty; public string ErrorMessage { get; set; } public RequireEitherAttribute(string compareProperty) { CompareProperty = compareProperty; } } public class StudentLogin { [DisplayName("Last Name")] [Required(ErrorMessage = "You must supply your last name.")] public string LastName { get; set; } [DisplayName("Student ID")] [RegularExpression(@"^\d{1,8}$", ErrorMessage = "Invalid Student ID")] [RequireEither("SSN", ErrorMessage = "You must supply your student id or social security number.")] public int? StudentId { get; set; } [DisplayName("Social Security Number")] [RegularExpression(@"^\d{9}|\d{3}-\d{2}-\d{4}$", ErrorMessage = "Invalid Social Security Number")] public string SSN { get; set; } } 

我的看法:

   

Please supply the following information to login:

  1. x.LastName) %>
    x.LastName)%> x.LastName) %>

  2. x.StudentId) %>
    x.StudentId) %> x.StudentId) %>

    - OR -

    x.SSN)%>
    x.SSN) %> x.SSN) %>

解决这个问题的一种方法不仅仅是创建ValidationAttribute并在类级别应用它。

 [RequireEither("StudentId", "SSN")] public class StudentLogin 

错误消息将自动显示在“validation摘要”中。 该属性看起来像这样(我通过将所有内容视为字符串只是为了简洁,大大简化了IsValid()中的validation逻辑:

 public class RequireEither : ValidationAttribute { private string firstProperty; private string secondProperty; public RequireEither(string firstProperty, string secondProperty) { this.firstProperty = firstProperty; this.secondProperty = secondProperty; } public override bool IsValid(object value) { var firstValue = value.GetType().GetProperty(this.firstProperty).GetValue(value, null) as string; var secondValue = value.GetType().GetProperty(this.secondProperty).GetValue(value, null) as string; if (!string.IsNullOrWhiteSpace(firstValue)) { return true; } if (!string.IsNullOrWhiteSpace(secondValue)) { return true; } // neither was supplied so it's not valid return false; } } 

请注意,在这种情况下,传递给IsValid()的对象是类本身的实例而不是属性。

我喜欢Steve和Ronnie的解决方案,虽然他们创建的自定义属性可能用于其他类/属性对,但我不喜欢这个简单案例的“魔术字符串”和reflection,我通常创建一个适合的validation手头的情景。

例如,在这种情况下,我会创建类似于:

 [AttributeUsage(AttributeTargets.Class)] public class RequireStudentInfoAttribute : ValidationAttribute { public override bool IsValid(object value) { var student = value as StudentLogin; if(student == null) { return false; } if (student.StudentId.HasValue || !string.IsNullOrEmpty(student.SSN)) { return true; } return false; } } 

只需将它应用于StudentLogin类,如:

 [RequireStudentInfo] public class StudentLogin 

关于客户端validation,我通常会访问http://xval.codeplex.com/ ,因为它与Data Annotations的集成非常好

使用史蒂夫的建议只做了一些细微的改变:

 public class RequireEitherAttribute : ValidationAttribute { private string firstProperty; private string secondProperty; public RequireEitherAttribute(string firstProperty, string secondProperty) { this.firstProperty = firstProperty; this.secondProperty = secondProperty; } public override bool IsValid(object value) { object firstValue = value.GetType().GetProperty(firstProperty).GetValue(value, null); object secondValue = value.GetType().GetProperty(secondProperty).GetValue(value, null); return InputSupplied(firstValue) || InputSupplied(secondValue); } private bool InputSupplied(object obj) { if (obj == null) return false; if (obj is string) { string str = (string)obj; if (str.Trim() == string.Empty) return false; } return true; } } 

由于这不是属性级别validation,因此我必须向视图添加validation摘要。

我仍然很好奇如何将其与客户端validation联系起来。