从Lambda Property Expression获取自定义属性

时间:2009-10-13 12:06:44

标签: c# attributes lambda

我正在使用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>"));
}

2 个答案:

答案 0 :(得分:44)

你的表达式解析逻辑可以使用一些工作。您将转换为字符串,而不是处理实际类型。

以下是一组您可能会使用的扩展方法。第一个获取成员的名称。第二个/第三个组合以检查属性是否在成员上。 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;
    }
}

希望这可以帮助你。

答案 1 :(得分:6)

这段代码(来自codeplex上的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;
    }