2009-10-13 9 views
19

Ich verwende ASP.NET MVC 2 Preview 2 und habe eine benutzerdefinierte HtmlHelper-Erweiterungsmethode geschrieben, um mithilfe eines Ausdrucks eine Beschriftung zu erstellen. Das TModel stammt aus einer einfachen Klasse mit Eigenschaften und die Eigenschaften können Attribute haben, um Validierungsanforderungen zu definieren. Ich versuche herauszufinden, ob ein bestimmtes Attribut für die Eigenschaft existiert, die der Ausdruck in meiner Label-Methode darstellt.Benutzerdefinierte Attribute aus dem Lambda-Eigenschaftsausdruck abrufen

Der Code für die Klasse und Label:

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

Dann würde ich das Etikett wie folgt aufrufen:

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

Gibt es eine Möglichkeit in der Ausdruckswert, wenn die Eigenschaft, um herauszufinden, an die Label-Methode übergeben wurde, hat das Required-Attribut?

Ich fand heraus, dass das folgende tun mir das Attribut, wenn es existiert, aber ich hoffe, es gibt eine sauberere Möglichkeit, dies zu erreichen.

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

Antwort

40

Ihre Ausdrucksparsinglogik könnte etwas Arbeit gebrauchen. Anstatt mit den tatsächlichen Typen zu arbeiten, konvertieren Sie in Strings.

Hier ist eine Reihe von Erweiterungsmethoden, die Sie stattdessen verwenden könnten. Der erste erhält den Namen des Mitglieds. Der zweite/dritte Mähdrescher überprüft, ob das Attribut auf dem Mitglied ist. GetAttribute gibt das angeforderte Attribut oder den NULL-Wert zurück, und IsRequired prüft nur nach diesem bestimmten Attribut.

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

Hoffentlich hilft Ihnen das.

+0

Das ist viel besser, danke! Ist es möglich, GetAttribute zu einer Erweiterungsmethode des Ausdrucks zu ändern? Dies würde eine einfache Überprüfung eines Ausdrucks für ein Attribut ermöglichen. – Bernd

+0

+1 Großer Code Mann! Ich werde dies in meinem Buch "ASP.NET MVC Cookbook" erwähnen (http://groups.google.com/group/aspnet-mvc-2-cookbook-review) –

+0

Also habe ich diese Lösung für eine lange Zeit verwendet, aber kürzlich Ich habe es noch einmal gelesen, als ich mit EntityFrameworks "DbSet.Include" gearbeitet habe, das es versäumt hat, verschachtelte Eigenschaften richtig zu laden (zB 'Thing1.Thing2' von' o => o.Thing1.Thing2'). Es gibt eine [etwas robustere Version] (http://stackoverflow.com/a/2916344/1037948) von Ihnen, die "UnaryExpression" berücksichtigt, aber die Stringkonvertierung, die Sie vorgeschlagen haben, [scheint der einfachste Weg zu sein] (http : //stackoverflow.com/a/17220748/1037948) um ​​den "vollqualifizierten" Namen zu erhalten. – drzaus

6

Wie dieser Code (aus dem MVC-Projekt auf 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

Ich verstehe diesen Code nicht wirklich, aber ich habe ihn in meinen HtmlHelperExtensionMethods ausgeschnitten und eingefügt und er hat so funktioniert wie er ist. :) Die andere Lösung funktionierte nicht für mich, weil ich MetadataType verwende. – RitchieD

Verwandte Themen