6

Tôi tạo ra một HtmlHelper cho Label mà đặt một ngôi sao sau tên của Label rằng nếu lĩnh vực liên quan được yêu cầu:MVC HtmlHelper vs FluentValidation 3.1: Troubles nhận ModelMetadata isRequired

public static MvcHtmlString LabelForR<TModel, TValue>(
     this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression) 
{ 
    return LabelHelper(
     html, 
     ModelMetadata.FromLambdaExpression(expression, html.ViewData), 
     ExpressionHelper.GetExpressionText(expression), 
     null); 
} 

private static MvcHtmlString LabelHelper(HtmlHelper helper, ModelMetadata metadata, string htmlFieldName, string text) 
{ 
    ... //check metadata.IsRequired here 
    ... // if Required show the star 
} 

Nếu tôi sử dụng DataAnnotations và tát [Bắt buộc ] trên thuộc tính trong ViewModel của tôi, metadata.IsRequired trong LabelHelper riêng tư của tôi sẽ bằng True và mọi thứ sẽ hoạt động như dự định.

Tuy nhiên, nếu tôi sử dụng FluentValidation 3.1 và thêm một quy tắc đơn giản như thế:

public class CheckEmailViewModelValidator : AbstractValidator<CheckEmailViewModel> 
{ 
    public CheckEmailViewModelValidator() 
    { 
     RuleFor(m => m.Email) 
      .NotNull() 
      .EmailAddress(); 
    } 
} 

... trong LabelHelper metadata.IsRequired của tôi sẽ được sai thiết lập là false. (Trình xác nhận hoạt động mặc dù: bạn không thể gửi trường trống và nó cần phải là một Email như thế).
Phần còn lại của siêu dữ liệu có vẻ chính xác (Ví dụ: metadata.DisplayName = "Email").
Về lý thuyết, FluentValidator slaps RequiredAttribute trên thuộc tính nếu Rule .NotNull() được sử dụng.

Đối với tài liệu tham khảo: ViewModel của tôi:

[Validator(typeof(CheckEmailViewModelValidator))] 
public class CheckEmailViewModel 
{ 
    //[Required] 
    [Display(Name = "Email")] 
    public string Email { get; set; } 
} 

điều khiển của tôi:

public class MemberController : Controller 
{ 
    [HttpGet] 
    public ActionResult CheckEmail() 
    { 
     var model = new CheckEmailViewModel(); 
     return View(model); 
    } 
} 

Any help is appreciated.

+0

Bạn đã giải quyết vấn đề này chưa? –

+0

Chỉ để mở rộng nhận xét của Jeremy. Tôi đã không viết "thực hiện tùy chỉnh của ModelMetadataProvider của MVC mà biết cách thẩm vấn các lớp trình duyệt tính hợp lệ" về cơ bản vì tôi không biết ngay lập tức làm thế nào để làm điều đó và nghiên cứu nó có lẽ sẽ mất rất nhiều thời gian. Nếu bạn có thể cung cấp một ví dụ về điều này, nó hoàn toàn sẽ giúp bạn! – Dmitry

+0

Tôi cũng không biết.Tôi đã mở rộng LabelFor và thêm lớp vào nó. Nó không phải là giải pháp tốt nhất, nhưng tôi không có thời gian để điều tra cách triển khai ModelMetadataProvider. Nhưng nếu tôi làm điều đó, tôi sẽ viết giải pháp ở đây. –

Trả lời

4

Theo mặc định, MVC sử dụng thuộc tính DataAnnotations cho hai mục đích riêng biệt - siêu dữ liệu và xác thực.

Khi bạn bật FluentValidation trong ứng dụng MVC, FluentValidation móc vào cơ sở hạ tầng xác thực chứ không phải siêu dữ liệu - MVC sẽ tiếp tục sử dụng thuộc tính cho siêu dữ liệu. Nếu bạn muốn sử dụng FluentValidation cho siêu dữ liệu cũng như xác thực thì bạn cần phải viết một triển khai tùy chỉnh của ModelMetadataProvider của MVC để biết cách thẩm vấn các lớp trình xác nhận hợp lệ - đây không phải là thứ mà FluentValidation hỗ trợ ra khỏi hộp.

3

Tôi có một ModelMetadataProvider tùy chỉnh mà tăng cường DataAnnotations mặc định một đưa ra sau đây:

  1. populates "DisplayName" từ chuỗi PROPERTYNAME tách từ Camel Case, nếu không chỉ định thông qua DisplayAttribute.
  2. Nếu ModelMetadata.IsRequired được đặt thành false, nó sẽ kiểm tra nếu có bất kỳ quy tắc xác thực thông thạo nào hiện diện (loại NotNull hoặc NotEmpty).

Tôi chắc chắn đã kiểm tra mã nguồn mà Jeremy đã chuẩn bị nhưng tôi chưa sẵn sàng cho tổng đại tu nên tôi trộn lẫn và khớp để không làm mất hành vi mặc định. Bạn có thể tìm thấy nó here

Đây là mã có một số tính năng bổ sung được lấy từ bài đăng this.

public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider 
{ 
    readonly IValidatorFactory factory; 
    public CustomModelMetadataProvider(IValidatorFactory factory) 
     : base() { 
     this.factory = factory; 
    } 

    // Uppercase followed by lowercase but not on existing word boundary (eg. the start) 
    Regex _camelCaseRegex = new Regex(@"\B\p{Lu}\p{Ll}", RegexOptions.Compiled); 
    // Creates a nice DisplayName from the model’s property name if one hasn't been specified 

    protected override ModelMetadata GetMetadataForProperty(
     Func<object> modelAccessor, 
     Type containerType, 
     PropertyDescriptor propertyDescriptor) { 

     ModelMetadata metadata = base.GetMetadataForProperty(modelAccessor, containerType, propertyDescriptor); 
     metadata.IsRequired = metadata.IsRequired || IsNotEmpty(containerType, propertyDescriptor.Name); 
     if (metadata.DisplayName == null) 
      metadata.DisplayName = displayNameFromCamelCase(metadata.GetDisplayName()); 

     if (string.IsNullOrWhiteSpace(metadata.DisplayFormatString) && 
      (propertyDescriptor.PropertyType == typeof(DateTime) || propertyDescriptor.PropertyType == typeof(DateTime?))) { 
      metadata.DisplayFormatString = "{0:d}"; 
     } 

     return metadata; 
    } 

    string displayNameFromCamelCase(string name) { 
     name = _camelCaseRegex.Replace(name, " $0"); 
     if (name.EndsWith(" Id")) 
      name = name.Substring(0, name.Length - 3); 
     return name; 
    } 

    bool IsNotEmpty(Type type, string name) { 
     bool notEmpty = false; 
     var validator = factory.GetValidator(type); 

     if (validator == null) 
      return false; 

     IEnumerable<IPropertyValidator> validators = validator.CreateDescriptor().GetValidatorsForMember(name); 

     notEmpty = validators.OfType<INotNullValidator>().Cast<IPropertyValidator>() 
          .Concat(validators.OfType<INotEmptyValidator>().Cast<IPropertyValidator>()).Count() > 0; 
     return notEmpty; 
    } 
} 
+0

Cảm ơn bạn đã đăng bài. – Ted

+0

«IValidatorFactory' nào mà bạn truyền vào hàm tạo cho' CustomModelMetaDataProvider' của bạn? – skeletank

+0

@skeletank Đó là một sự trừu tượng xác thực thông thạo. Như tôi nhớ, nó được sử dụng để giải quyết các cá thể xác nhận hợp lệ từ sự lựa chọn của bạn về vùng chứa DI. Tôi không biết nếu điều này đã thay đổi một cách nào đó kể từ phiên bản 3 vì đây là bài đăng cũ 3 năm – cleftheris

Các vấn đề liên quan