【问题标题】:ASP.NET MVC Iterate over Model Properties with Data AnnotationsASP.NET MVC 使用数据注释迭代模型属性
【发布时间】:2017-05-09 01:31:13
【问题描述】:

问题描述

我的问题类似于this question,但不是通过反射将数据注释应用于属性名称(由 ModelMetadata.DisplayName 处理),而是将它们应用于值(不由 ModelMetadata 处理)。

详细说明

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

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

然后假设这个模型至少用在 5 个不同的视图中,其中每个属性的值都必须完整显示。 (有时是每个实例,有时是少数,有时是一个)。

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

但是,如果稍后模型的定义扩展为包含新属性(例如描述和关系以及可靠性),那将无济于事。然后我需要手动更新每次出现的完整模型列表。

我可以使用反射来遍历 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()) 以及在 x 上 .Invokeing 它。
    [/Edit]
  • 正常手动检索值,然后
    • 按照this question 的建议,在插入视图元素之前对其执行一个方法来手动检索和实施注释数据,或者
    • 根据需要在显示模板视图中重新实现 DisplayFor 处理数据注释,并按照 this question 的建议通过 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"]) 的属性有点不直观之外,这似乎是最可行的解决方案,但以模型清晰度为代价。

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

问题

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

【问题讨论】:

  • 用自定义属性装饰您的模型,并编写代码来说明如何渲染每个模型。你为什么要这样做
  • 正如我所说,这纯粹是一种教育练习。我正在从一个教程构建一个简单的项目,以熟悉 MVC 工作原理的 ASP.NET 版本,然后我想“如果使用 MVC 的全部目的是分离关注点,为什么视图必须对每一个单独的模型上的属性名称,而从概念上讲,他们想要做的只是“显示所有属性的值,或某些生成的属性子集”?”所以我决定看看我能不能做到这一点。
  • 我建议的两个解决方案实际上是通过各种方式自己实现格式设置,(尽管使用自定义属性与数据注释存在相同的问题,因为它们不容易通过 PropertyInfo 访问)...但如果可能的话,我宁愿避免在这个意义上重新发明轮子,因为 Html webhelper 模板已经为直接属性访问定义了所有这些功能。
  • 它没有深入的知识——只是从模型中读取。当然总会有一些耦合,否则类本身不会很有用。这个想法是最小化耦合,而不是完全删除它。但是有这种想法对你有好处。
  • 谢谢。最小化耦合正是我试图通过迭代由其他地方的逻辑创建的集合来替换显式名称引用来实现的目标。

标签: c# asp.net-mvc reflection data-annotations


【解决方案1】:

可能类似于:

@foreach (var property in Model.GetType().GetProperties())
{
    <li>@property.GetValue(Model, null)</li>
}

【讨论】:

  • 除了添加null参数(据我所知并没有改变任何东西),这正是我一直在使用的,但它仍然存在不应用的问题以这种方式访问​​时的数据注释格式。
【解决方案2】:

大获成功

重新审视我最初尝试动态地手动制作表达式,我发现this article 正是我想做的,并且主要使用 Microsoft 提供的代码!

虽然微软的代码很难找到(文章中的链接坏了,代码我did find的例子稍微过时了),但我能够很好地使用它来实现我自己的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>
        }
}

与 _DisplayForReflectedProperty 一样简单

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

@model Tuple<Thing, PropertyInfo>

@Html.DisplayFor("Item1."+Model.Item2.Name)

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

var lambda = System.Linq.Dynamic.DynamicExpression.ParseLambda(typeof(TModel), 
             null, expression);

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

【讨论】:

    猜你喜欢
    • 2011-05-21
    • 2011-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-14
    • 1970-01-01
    • 1970-01-01
    • 2011-04-11
    相关资源
    最近更新 更多