2009-10-13 3 views
19

ASP.NET MVC 2 미리보기 2를 사용하고 식을 사용하여 레이블을 만드는 데 사용자 지정 HtmlHelper 확장 메서드를 작성했습니다. TModel은 속성을 가진 간단한 클래스에서 가져온 것이고 속성은 유효성 검사 요구 사항을 정의하는 속성을 가질 수 있습니다. 내 레이블 메서드에서 표현하는 속성에 특정 특성이 있는지 확인하려고합니다.람다 속성 식에서 사용자 지정 특성 얻기

클래스와 라벨의 코드는 다음과 같습니다

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

그 다음이 같은 라벨 부를 것이다 : 있는지 확인하는 방법이 있나요

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

을 표현 값의 특성 Label 메서드에 전달 된 필수 특성이 있습니까?

나는 다음과 같이하면 속성을 얻을 수 있다고 생각했지만, 이것을 수행하는 더 깨끗한 방법이있을 것으로 기대한다.

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

답변

40

표현 구문 분석 논리에서 일부 작업을 사용할 수 있습니다. 실제 유형을 처리하는 대신 문자열로 변환합니다.

다음은 대신 사용할 수있는 확장 방법 집합입니다. 첫 번째 멤버의 이름을 가져옵니다. 두 번째/세 번째 조합은 속성이 구성원에 있는지 확인합니다. GetAttribute는 요청 된 특성 또는 null을 반환하고 IsRequired는 해당 특성을 확인합니다.

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

이 정보가 도움이되기를 바랍니다.

+0

이 훨씬 더, 감사에 대한! GetAttribute를 Expression의 확장 메서드로 변경할 수 있습니까? 그러면 속성에 대한 표현식을 쉽게 점검 할 수 있습니다. – Bernd

+0

+1 훌륭한 코드 맨! 내 책 "ASP.NET MVC Cookbook"(http://groups.google.com/group/aspnet-mvc-2-cookbook-review) –

+0

에서이 문제를 언급 할 것이므로이 솔루션을 오랫동안 사용했지만 최근에는 EntityFramework의'DbSet.Include'를 사용하여 중첩 된 속성 (예 :'o => o.Thing1.Thing2'의'Thing1.Thing2')을 제대로 열지 못하는 경우 다시 작업합니다. 'UnaryExpression'을 고려한 [약간 더 강력한 버전] (http://stackoverflow.com/a/2916344/1037948)이 있습니다. 그러나 [가장 쉬운 방법 인 것처럼 보이는] 문자열 변환 (http : //stackoverflow.com/a/17220748/1037948) "정규화 된"이름을 가져옵니다. – drzaus

6

하는 방법 (코드 플렉스에 MVC 프로젝트에서)이 코드

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

이 코드는 실제로 이해할 수 없지만 잘라내어 HtmlHelperExtensionMethods에 붙여 넣으면 그대로 작동합니다. :) 다른 솔루션은 MetadataType을 사용하기 때문에 저에게 적합하지 않았습니다. – RitchieD

관련 문제