ASP.NET MVC使用数据批注遍历模型属性

Rob*_*rkk 5 c# reflection asp.net-mvc data-annotations

问题描述

我的问题类似于此问题,但不是通过反射将Data Annotations应用于属性Name(由ModelMetadata.DisplayName处理),而是将其应用于值(未由ModelMetadata处理)。

详细说明

在ASP.NET MVC程序的上下文中。
假设我有一个Model类

public class Model
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string NickName { get; set; }
    public string Address { get; set; }
    public int Phone { get; set; }

    [Display(Name = "Start Date")]
    [DataType(DataType.Date)]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
    public DateTime StartDate { get; set }

    [Display(Name = "End Date")]
    [DataType(DataType.Date)]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
    public DateTime EndDate { get; set }
}
Run Code Online (Sandbox Code Playgroud)

然后,假设该模型至少在5个不同的视图中使用,其中每个属性的值都必须完整显示。(有时是每种情况,有时是几次,一次是)。

我可以手动列出每个
<td>@Html.DisplayFor(item.<Property>)</td>
视图中每个属性的访问权限。

但这对以后的Model定义扩展为包括新的Properties(例如Description,Relation and Reliability)不会帮助我。然后,我需要手动更新每次出现的完整Model清单。

我可以使用反射来遍历PropertyInfo的列表,并节省使用以下方法手动列出每个属性的麻烦
<td>@property.GetValue(item)</td>

但DisplayFor(x)不支持表达复杂的x=>property.GetValue(item),而这意味着我失去了数据注释该格式我的日期时间作为
01/01/1990
代替
01-Jan-90 12:00:00 AM
,并可能也会导致所有类型的注释,包括确认的损失。

问题解决方案

到目前为止,我已经考虑(并在某些情况下尝试过)以下解决方案:

  • [失败]手动制作一个模仿[编辑]功能的表达式@property.GetValue(item)
  • [失败]传递DisplayFor代表属性访问器的MethodInfo对象DisplayFor(x => property.GetGetMethod()),并.Invoke在x上对其进行显示。
    [/编辑]
  • 照常手动获取值,然后
    • 按照此问题的建议,在插入到视图元素之前,对其执行一个方法以手动检索并在其上实现注释数据,或者
    • 重新实现DisplayFor在显示模板查看的需要的基础上处理数据注释,并直接应用,为通过DisplayFor值作为这个问题的建议
  • 重构Model类,使其仅包含“ Prop”实例的列表(SortedList?),其中“ Prop”是表示具有Name和Value元素的Property的类。

最后一个解决方案将把解决方案
@Html.DisplayFor(m=>property.GetValue(item)
转变为理论上
@Html.DisplayFor(m=>item.Properties[i].Value)
可行的方案,除了通过(.Value)获取名称为Name(Properties [“ Name”])的属性的需求有点不直观之外,这似乎是最可行的解决方案,但以模型清晰为代价。

[编辑]
最近,我创建了一个Utility方法,该方法从PropertyInfo中检索DisplayFormatAttribute,如果未注释格式字符串,则返回DisplayFormatString或默认值“ {0}”。然后,我用它在ViewModel中创建了一组预格式化的属性值。就目前而言,这似乎是我所知道的最优雅的方法,即在仍从视图中检索必要数据的同时,尽可能地将视图与模型分离。
[/编辑]

问题

目前,这纯粹是一种学习练习,但我想知道...
在失败的地方取得成功,并且都拥有自己的Reflection蛋糕并且也可以吃数据注释,这是否有可能成功?还是我必须寻求替代解决方案?
如果我必须寻求替代解决方案,是我错过了某些路线,还是至少在正确的轨道上?

Rob*_*rkk 0

巨大的成功

重新审视我最初动态手动创建表达式的尝试,我发现这篇文章正是我想做的,并且主要使用了 Microsoft 提供的代码!

尽管很难找到 Microsoft 代码(文章中的链接已损坏,并且示例对于我找到的代码来说稍微过时),但我能够使用它来实现我自己的 DisplayFor 扩展方法,效果良好。
不幸的是,由于我的模型是一个列表而不是单个实例,我仍然需要创建一个部分视图来传递实例,以便我可以从生成的表达式中通过模型访问属性。

我的视图代码现在如下所示:

@foreach (var thing in Model.CollectionOfThings)
{
    <tr>
        @foreach (var prop in typeof(Thing).GetProperties())
        {
            <td>
                @{
                    Html.RenderPartial("~/Views/Shared/_DisplayForReflectedProperty.cshtml", 
                    new Tuple<Thing, PropertyInfo>(thing, prop));
                }
            </td>
        }
}
Run Code Online (Sandbox Code Playgroud)

使用 _DisplayForReflectedProperty 就这么简单

@using WebApplication1.Models
@using System.Reflection
@using WebApplication1.Extensions

@model Tuple<Thing, PropertyInfo>

@Html.DisplayFor("Item1."+Model.Item2.Name)
Run Code Online (Sandbox Code Playgroud)

我的 DisplayFor 扩展与本文中的扩展之间的唯一区别是此函数调用中的空对象参数(加上从 EditorFor 到 DisplayFor 的明显转换):

var lambda = System.Linq.Dynamic.DynamicExpression.ParseLambda(typeof(TModel), 
             null, expression);
Run Code Online (Sandbox Code Playgroud)

使用此模板,我现在可以生成模型属性的任意(从代码角度来看,从业务规则角度来看特定)子集,以我希望的任何方式显示,而无需针对每个特定子集定制视图,同时保留使用“For”助手!