如何使 WebGrid 列采用真正的 lambda 来实现类型安全?

How can I make WebGrid columns take a real lambda to be typesafe?

创建 WebGridColumn 时,需要为将要使用的 columnName 传入一个字符串。为什么它不允许像大多数其他 HTML 助手一样传递 lambda 表达式?我如何传递一个简单的 lambda 以获得智能提示、保留 type-safety 并防止因拼写错误导致的运行时错误?有了列名,WebGrid 难道不应该能够使用元数据显示属性来默认设置 header 和格式吗?

我们经常使用的东西:

@{ var grid = new WebGrid(model) }

@grid.GetHtml(
    // table style formats here...

    columns: grid.Columns(
        grid.Column(columnName: "StudentName",
                    header: Html.DisplayNameFor(model => model.StudentName)),
        grid.Column(columnName: "Birthdate",
                    header: Html.DisplayNameFor(model => model.Birthdate),
                    format: (item) => Html.DisplayFor(modelItem => (item as WebGridRow).Value as Person).Birthdate),
        grid.Column(columnName: "Address.Physical.StreetNumber",
                    header: Html.DisplayFor(model => model.Address.Physical.StreetNumber)),
        grid.Column(columnName: "Address.Physical.StreetName",
                    header: Html.DisplayFor(model => model.Address.Physical.StreetName))
    )
)

这是一位同事向我提出的问题。我们广泛使用 WebGrid,并且必须为每一列重复输入相同的信息一式两份或一式三份……至少可以说是令人沮丧。我研究了扩展 WebGrid 本身,但似乎要使通用和 type-safe 需要大量工作。相反,我写了一个 HtmlHelper 扩展 returns a WebGridColumn.

代码

public static WebGridColumn GridColumnFor<TModel, TValue>(this HtmlHelper<IEnumerable<TModel>> html, Expression<Func<TModel, TValue>> exp, string header = null, Func<dynamic, object> format = null, string style = null, bool canSort = true)
{
    var metadata = ModelMetadata.FromLambdaExpression(exp, new ViewDataDictionary<TModel>());
    var modelText = ExpressionHelper.GetExpressionText(exp);

    if (format == null && metadata.DisplayFormatString != null)
    {
        format = (item) => string.Format(metadata.DisplayFormatString, item[modelText] ?? String.Empty);
    }

    return new WebGridColumn()
    {
        ColumnName = modelText,
        Header = header ?? metadata.DisplayName ?? metadata.PropertyName ?? modelText.Split('.').Last(),
        Style = style,
        CanSort = canSort,
        Format = format
    };
}

任何您通常使用 WebGrid.Column() 的地方都可以使用 Html.GridColumnFor()

columns: grid.Columns(
    Html.GridColumnFor(model => model.Name),
    Html.GridColumnFor(model => model.Birthdate),
    Html.GridColumnFor(model => model.Address.Physical.StreetNumber),
    Html.GridColumnFor(model => model.Address.Physical.StreetName),
)

真漂亮...

怎么回事?

它使用 built-in 表达式功能获取完整的 属性 名称以传递给 WebGridColumn 构造函数,并从 MVC 获取已经 built-in 的元数据以获取要显示的名称headers,并检查格式。

它也接受 header 和格式值的覆盖,所以你可以自己设置它们;您可能希望一个人 object 用于多个页面,但有些人将 Birthdate 字段称为 "Birthday",而其他人可能将其称为 "Date of Birth"(我们尽量避免这种情况,但是有时这是最终用户的要求),或者只显示日期和月份:

Html.GridColumnFor(model => model.Birthdate, format: (item) => item.Birthdate.ToString("MM/dd"))