Получить пользовательские атрибуты из выражения лямбда-свойства

Я использую ASP.NET MVC 2 Preview 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, атрибут Required?

Я понял, что выполнение следующего действительно дает мне атрибут, если он существует, но я надеюсь, что есть более чистый способ сделать это.

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>"));
}
13.10.2009 12:06:44
2 ОТВЕТА
РЕШЕНИЕ

Ваша логика синтаксического анализа выражения может использовать некоторую работу. Вместо того, чтобы иметь дело с реальными типами, вы конвертируете в строки.

Вот набор методов расширения, которые вы можете использовать вместо этого. Первый получает имя участника. Второй / третий объединяются, чтобы проверить, есть ли атрибут на элементе. GetAttribute вернет запрошенный атрибут или ноль, а 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;
    }
}

Надеюсь, это поможет вам.

45
13.10.2009 15:24:31
Это намного лучше, спасибо! Можно ли изменить GetAttribute на метод расширения Expression? Это позволит легко проверить любое выражение для атрибута.
Bernd 15.10.2009 11:55:51
+1 Отличный код человек! Я упомяну об этом в моей книге «ASP.NET MVC Cookbook» ( groups.google.com/group/aspnet-mvc-2-cookbook-review )
Andrew Siemer 17.05.2010 03:36:07
Поэтому я использовал это решение в течение длительного времени, но в последнее время пересматривал при работе с EntityFramework - х DbSet.Include, который не может правильно нетерпеливые нагрузки вложенных свойств (т.е. Thing1.Thing2из o => o.Thing1.Thing2). Есть немного более надежная версия, которая учитывает UnaryExpression, но, похоже, избегать преобразования строк, которое вы предлагаете , - самый простой способ получить «полностью определенное» имя.
drzaus 20.06.2013 18:33:30

Как насчет этого кода (из проекта MVC на 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;
    }
6
25.11.2010 16:58:30
Я не очень понимаю этот код, но я вырезал и вставил его в мои HtmlHelperExtensionMethods, и он работал как есть. :) Другое решение не работает для меня, потому что я использую MetadataType.
RitchieD 31.10.2014 13:40:34