2009-10-13 36 views
19

Tôi đang sử dụng ASP.NET MVC 2 Preview 2 và đã viết phương thức mở rộng tùy chỉnh HtmlHelper để tạo nhãn bằng cách sử dụng biểu thức. TModel là từ một lớp đơn giản với các thuộc tính và các thuộc tính có thể có các thuộc tính để xác định các yêu cầu xác nhận hợp lệ. Tôi đang cố gắng tìm hiểu xem một thuộc tính nào đó có tồn tại trên thuộc tính hay không biểu thức thể hiện trong phương thức nhãn của tôi.Nhận các thuộc tính tùy chỉnh từ Biểu thức thuộc tính Lambda

Mã cho lớp và nhãn là:

public class MyViewModel 
{ 
    [Required] 
    public string MyProperty { get; set; } 
} 

public static MvcHtmlString Label<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string label) 
{ 
    return MvcHtmlString.Create(string.Concat("<label for=\"", expression.GetInputName(), "\">", label, "</label>")); 
} 

public static string GetInputName<TModel, TProperty>(this Expression<Func<TModel, TProperty>> expression) 
{ 
    return expression.Body.ToString().Substring(expression.Parameters[0].Name.Length + 1); 
} 

Sau đó, tôi sẽ gọi nhãn như thế này:

Html.Label(x => x.MyProperty, "My Label") 

Có cách nào để tìm hiểu xem thuộc tính trong giá trị biểu được chuyển đến phương thức Nhãn có thuộc tính Bắt buộc?

Tôi đã tìm ra rằng thực hiện những điều sau đây không cho tôi thuộc tính nếu nó tồn tại, nhưng tôi hy vọng có một cách sạch hơn để thực hiện việc này.

public static MvcHtmlString Label<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string label) 
{ 
    System.Attribute.GetCustomAttribute(Expression.Property(Expression.Parameter(expression.Parameters[0].Type, expression.GetInputName()), expression.GetInputName()).Member, typeof(RequiredAttribute)) 

    return MvcHtmlString.Create(string.Concat("<label for=\"", expression.GetInputName(), "\">", label, "</label>")); 
} 

Trả lời

40

Logic phân tích cú pháp biểu thức của bạn có thể sử dụng một số công việc. Thay vì đối phó với các loại thực tế, bạn đang chuyển đổi thành chuỗi.

Dưới đây là một bộ phương pháp tiện ích mở rộng mà bạn có thể sử dụng thay thế. Đầu tiên là tên của thành viên. Thứ hai/thứ ba kết hợp để kiểm tra xem thuộc tính có nằm trong thành viên hay không. GetAttribute sẽ trả về thuộc tính được yêu cầu hoặc null và IsRequired chỉ kiểm tra thuộc tính cụ thể đó.

public static class ExpressionHelpers 
{ 
    public static string MemberName<T, V>(this Expression<Func<T, V>> expression) 
    { 
     var memberExpression = expression.Body as MemberExpression; 
     if (memberExpression == null) 
      throw new InvalidOperationException("Expression must be a member expression"); 

     return memberExpression.Member.Name; 
    } 

    public static T GetAttribute<T>(this ICustomAttributeProvider provider) 
     where T : Attribute 
    { 
     var attributes = provider.GetCustomAttributes(typeof(T), true); 
     return attributes.Length > 0 ? attributes[0] as T : null; 
    } 

    public static bool IsRequired<T, V>(this Expression<Func<T, V>> expression) 
    { 
     var memberExpression = expression.Body as MemberExpression; 
     if (memberExpression == null) 
      throw new InvalidOperationException("Expression must be a member expression"); 

     return memberExpression.Member.GetAttribute<RequiredAttribute>() != null; 
    } 
} 

Hy vọng điều này sẽ giúp bạn.

+0

này là tốt hơn nhiều, cảm ơn! Có thể thay đổi GetAttribute thành một phương thức mở rộng của Expression không? Điều đó sẽ cho phép dễ dàng kiểm tra bất kỳ biểu thức nào cho một thuộc tính. – Bernd

+0

+1 Người viết mã tuyệt vời! Tôi sẽ đề cập đến điều này trong cuốn sách "ASP.NET MVC Cookbook" (http://groups.google.com/group/aspnet-mvc-2-cookbook-review) –

+0

Vì vậy, tôi đã sử dụng giải pháp này trong một thời gian dài, nhưng gần đây xem lại nó khi làm việc với 'DbSet.Include' của EntityFramework, không tải đúng các thuộc tính lồng nhau (ví dụ' Thing1.Thing2' của 'o => o.Thing1.Thing2'). Có một [phiên bản mạnh hơn một chút] (http://stackoverflow.com/a/2916344/1037948) của bạn có tính đến "UnaryExpression', nhưng chuyển đổi chuỗi bạn đề xuất tránh [có vẻ là cách dễ nhất] (http : //stackoverflow.com/a/17220748/1037948) để nhận tên "đủ điều kiện". – drzaus

6

Làm thế nào về mã này (từ dự án MVC trên CodePlex)

public static bool IsRequired<T, V>(this Expression<Func<T, V>> expression, HtmlHelper<T> htmlHelper) 
    { 
     var modelMetadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); 
     string modelName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(expression)); 
     FieldValidationMetadata fieldMetadata = ApplyFieldValidationMetadata(htmlHelper, modelMetadata, modelName); 
     foreach (var item in fieldMetadata.ValidationRules) 
     { 
      if (item.ValidationType == "required") 
       return true; 
     } 

     return false; 
    } 

    private static FieldValidationMetadata ApplyFieldValidationMetadata(HtmlHelper htmlHelper, ModelMetadata modelMetadata, string modelName) 
    { 
     FormContext formContext = htmlHelper.ViewContext.FormContext; 
     FieldValidationMetadata fieldMetadata = formContext.GetValidationMetadataForField(modelName, true /* createIfNotFound */); 

     // write rules to context object 
     IEnumerable<ModelValidator> validators = ModelValidatorProviders.Providers.GetValidators(modelMetadata, htmlHelper.ViewContext); 
     foreach (ModelClientValidationRule rule in validators.SelectMany(v => v.GetClientValidationRules())) 
     { 
      fieldMetadata.ValidationRules.Add(rule); 
     } 

     return fieldMetadata; 
    } 
+0

Tôi không thực sự hiểu mã này, nhưng tôi cắt và dán nó trong HtmlHelperExtensionMethods của tôi và nó đã làm việc như là. :) Các giải pháp khác đã không làm việc cho tôi vì tôi sử dụng MetadataType. – RitchieD

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