如何在.NET MVC中读取属性数据注释值

时间:2011-11-13 14:22:10

标签: asp.net-mvc-3

我刚开始使用ASP.NET MVC 3,我试图在创建/编辑视图上为ViewModel上的字符串属性渲染以下HTML。

<input id="PatientID" name="PatientID" placeholder="Patient ID" type="text" value="" maxlength="30" />

每个值都与ViewModel上的属性绑定,id&amp; name是属性名称,占位符是Display属性,value是属性的值,maxlength是StringLength属性。

我没有输入上面的HTML,而是为每个字符串属性输入正确的值,我想我会尝试使用SingleLineTextBox的名称创建一个EditorTemplate,并在我的字符串属性上使用UIHint或者在传递视图的名称时我叫EditFor。到目前为止一切顺利,除了我无法弄清楚如何从StringLength属性中获取maxlength值。

这是我到目前为止的代码:

<input id="@ViewData.ModelMetadata.PropertyName" name="@ViewData.ModelMetadata.PropertyName" placeholder="@ViewData.ModelMetadata.DisplayName" type="text" value="@ViewData.Model" maxlength="??" />

如您所见,不确定如何设置maxlength值。谁知道怎么做?

另外,我是最好的方式吗?正如我之前所说,我可以自己为页面上的每个属性写出纯HTML。我已经看过使用TextBox因为它没有设置maxlength并且因为我不想要的StringLength属性而在HTML输出中添加了一堆验证标记。我看到的另一个选项是HTML类的扩展/帮助。

6 个答案:

答案 0 :(得分:7)

tvanfosson答案的完整代码示例:

型号:

public class Product
{
    public int Id { get; set; }

    [MaxLength(200)]
    public string Name { get; set; }

EditorTemplates \ String.cshtml

@model System.String
@{
    var metadata = ViewData.ModelMetadata;
    var prop = metadata.ContainerType.GetProperty(metadata.PropertyName);
    var attrs = prop.GetCustomAttributes(false);

    var maxLength = attrs.OfType<System.ComponentModel.DataAnnotations.MaxLengthAttribute>().FirstOrDefault();
}
<input id=@Html.IdForModel()@(metadata.IsRequired ? " required" : "")@(maxLength == null ? "" : " maxlength=" + maxLength.Length) />

HTML输出:

<input id=Name maxlength=200 />

丑陋但它有效。现在让我们抽象它并清理一下。助手班:

public static class EditorTemplateHelper
{
    public static PropertyInfo GetPropertyInfo(ViewDataDictionary viewData)
    {
        var metadata = viewData.ModelMetadata;
        var prop = metadata.ContainerType.GetProperty(metadata.PropertyName);
        return prop;
    }

    public static object[] GetAttributes(ViewDataDictionary viewData)
    {
        var prop = GetPropertyInfo(viewData);
        var attrs = prop.GetCustomAttributes(false);
        return attrs;
    }

    public static string GenerateAttributeHtml(ViewDataDictionary viewData, IEnumerable<Delegate> attributeTemplates)
    {
        var attributeMap = attributeTemplates.ToDictionary(t => t.Method.GetParameters()[0].ParameterType, t => t);
        var attrs = GetAttributes(viewData);

        var htmlAttrs = attrs.Where(a => attributeMap.ContainsKey(a.GetType()))
            .Select(a => attributeMap[a.GetType()].DynamicInvoke(a));

        string s = String.Join(" ", htmlAttrs);
        return s;
    }
}

编辑模板:

@model System.String
@using System.ComponentModel.DataAnnotations;
@using Brass9.Web.Mvc.EditorTemplateHelpers;
@{
    var metadata = ViewData.ModelMetadata;

    var attrs = EditorTemplateHelper.GenerateAttributes(ViewData, new Delegate[] {
        new Func<StringLengthAttribute, string>(len => "maxlength=" + len.MaximumLength),
        new Func<MaxLengthAttribute, string>(max => "maxlength=" + max.Length)
    });

    if (metadata.IsRequired)
    {
        attrs.Add("required");
    }

    string attrsHtml = String.Join(" ", attrs);
}
<input type=text id=@Html.IdForModel() @attrsHtml />

所以你传入一个Delegates数组,并为每个条目使用Func<AttributeTypeGoesHere, string>,然后为每个属性返回你想要的任何HTML字符串。

这实际上很好地解耦 - 您只能映射您关注的属性,您可以为同一HTML的不同部分映射不同的集合,最终用法(如@attrsHtml)不会损害可读性模板。

答案 1 :(得分:5)

您可以使用StringLength属性代替AdditionalMetadata属性(因为它是验证程序属性而非元数据提供程序)。样品用法:

public class ViewModel
{
    [AdditionalMetadata("maxLength", 30)]
    public string Property { get; set; }
}

基本上它将值30置于ViewData.ModelMetadata.AdditionalValues字典中的密钥maxLength下。所以你可以使用你的EditorTemplate:

<input maxlength="@ViewData.ModelMetadata.AdditionalValues["maxLength"]" id="@ViewData.ModelMetadata.PropertyName" name="@ViewData.ModelMetadata.PropertyName" placeholder="@ViewData.ModelMetadata.DisplayName" type="text" value="@ViewData.Model"  />

答案 2 :(得分:2)

要执行此操作,您需要创建自己的HtmlHelper扩展并使用反射来获取模型属性上的属性。查看http://codeplex.com/aspnet处的源代码,了解现有的...For() HtmlHelper扩展。您需要使用作为参数传入的表达式来获取model属性的PropertyInfo对象。他们有几个辅助类,应该作为模板。完成后,使用PropertyInfo上的GetCustomAttributes方法查找StringLength属性并提取它的值。由于您将使用TagBuilder创建输入,因此请通过TagBuilder将长度添加为属性。

   ...

   var attribute = propInfo.GetCustomAttributes(typeof(StringLengthAttribute),false)
                           .OfType<StringLengthAttribute>()
                           .FirstOrDefault();
   var length = attribute != null ? attribute.MaximumLength : 20; //provide a default
   builder.Attributes.Add("maxlength",length);

   ...

   return new MvcHtmlString( builder.ToString( TagRenderMode.SelfClosing ) );
}

请参阅我的评论,为什么我认为这是一个坏主意。

答案 3 :(得分:0)

更简单的解决方案是实现这样的自定义DataAnnotationsModelMetadataProvider

internal class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
    {
        ModelMetadata modelMetadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);

        var maxLengthAttribute = attributes.OfType<MaxLengthAttribute>().SingleOrDefault();
        if (maxLengthAttribute != null)
        {
            modelMetadata.AdditionalValues.Add("maxLength", maxLengthAttribute.Length);
        }
        return modelMetadata;
    }
}

在模板中,您只需使用:

object maxLength;
ViewData.ModelMetadata.AdditionalValues.TryGetValue("maxLength", out maxLength);

答案 4 :(得分:0)

可以编辑器模板中获取 StringLength 验证程序,以下是一些示例:

https://jefferytay.wordpress.com/2011/12/20/asp-net-mvc-string-editor-template-which-handles-the-stringlength-attribute/

我在上面的文章中使用和测试的结果可以在下面的答案中看到(使用MVC 5测试,EF 6):

ASP.NET MVC 3 - Data Annoation and Max Length/Size for Textbox Rendering

没有具体说明,我个人尝试实施其他一些方法时遇到了一些混合的结果,而且我没有发现任何声称的方法特别长;然而,我确实认为其他一些接近看起来有点“漂亮”。

答案 5 :(得分:0)

@using System.ComponentModel.DataAnnotations
@model string
@{
    var htmlAttributes = ViewData["htmlAttributes"] ?? new { @class = "checkbox-inline" };

    var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);

    if (!attributes.ContainsKey("maxlength"))
    {
        var metadata = ViewData.ModelMetadata;
        var prop = metadata.ContainerType.GetProperty(metadata.PropertyName);
        var attrs = prop.GetCustomAttributes(false);
        var maxLength = attrs.OfType<MaxLengthAttribute>().FirstOrDefault();
        if (maxLength != null)
        {
            attributes.Add("maxlength", maxLength.Length.ToString());
        }
        else
        {
            var stringLength = attrs.OfType<StringLengthAttribute>().FirstOrDefault();

            if (stringLength != null)
            {
                attributes.Add("maxlength", stringLength.MaximumLength.ToString());
            }
        }
    }

}

@Html.TextBoxFor(m => m, attributes)