2010-08-31 5 views
1

Я пытаюсь проверить, что в одном из двух полей текстового поля на моем представлении есть значение. Я сделал эту модель валидатора:ASP.NET MVC 2 Проблема с пользовательским ModelValidator

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

Эта логика проверки никогда не получает удар, и я думаю, что это потому, что значение не получает подается в текстовые поля.

В случае, если вам это нужно, вот поставщик и приписывать я создал наряду с использованием атрибута:

public class MyValidatorProvider : AssociatedValidatorProvider 
{ 
    protected override IEnumerable<ModelValidator> GetValidators(
     ModelMetadata metadata, ControllerContext context, 
     IEnumerable<Attribute> attributes) 
    { 
     foreach (var attrib in attributes.OfType<RequireEitherAttribute>()) 
      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; } 
} 

Мое мнение:

<%Html.BeginForm(); %> 
    <p> 
     Please supply the following information to login:</p> 
    <ol class="standard"> 
     <li> 
      <p> 
       <%=Html.LabelFor(x => x.LastName) %><br /> 
       <%=Html.TextBoxFor(x => x.LastName)%> 
       <%=Html.ValidationMessageFor(x => x.LastName) %></p> 
     </li> 
     <li> 
      <p> 
       <%=Html.LabelFor(x => x.StudentId) %><br /> 
       <%=Html.TextBoxFor(x => x.StudentId) %> 
       <%=Html.ValidationMessageFor(x => x.StudentId) %></p> 
      <p style="margin-left: 4em;"> 
       - OR -</p> 
      <p> 
       <%=Html.LabelFor(x => x.SSN)%><br /> 
       <%=Html.TextBoxFor(x => x.SSN) %> 
       <%=Html.ValidationMessageFor(x => x.SSN) %> 
      </p> 
     </li> 
    </ol> 
    <%=Html.SubmitButton("submit", "Login") %> 
    <%Html.EndForm(); %> 

ответ

1

Одним из способов решения этой проблемы является не просто создать ValidationAttribute и применить это на уровне класса.

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

Сообщение об ошибке будет автоматически отображаться в Сводке валидации. Атрибут будет выглядеть примерно так (я резко упрощена логика проверки внутри IsValid(), рассматривая все как строки только для краткости:

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() является экземпляр самого класса, а не собственность

+0

Мне это нравится. Я попробую. –

+0

Что касается проверки на стороне клиента? –

+0

Это определенно нуждается в специальной проверке клиента (хорошо известный пример здесь: http://haacked.com/archive/2009/11/19/aspnetmvc2-custom-validation.aspx). не уверен, насколько хорошо он будет работать с проверкой на уровне класса. –

0

Используется предложение Стива только с некоторыми незначительными изменениями:.

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

Поскольку это не проверка уровня собственности, я должен был добавить резюме проверки в целях .

Мне все еще интересно узнать, как подключить это с проверкой на стороне клиента.

+0

Что делать, если пользователь вводит пробел? Тогда простая проверка нуля будет недостаточной. –

+0

Спасибо. Исправлено, я думаю. –

1

Мне нравятся решения Стива и Ронни, и хотя пользовательский атрибут, который они создали, может использоваться для других классов/пар свойств, мне не нравятся «магические строки» и отражение для такого простого случая, и я обычно создаю валидация, которая оправдывает сценарий в руке.

К примеру, в этом случае я бы создал что-то вроде:

[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 

Что касается проверки на стороне клиента, я обычно хожу для http://xval.codeplex.com/, так как он отлично интегрируется с данными Аннотации

Смежные вопросы