如何使WebGrid列接受真正的Lambda以实现类型安全?

huangapple go评论135阅读模式
英文:

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

问题

在创建WebGridColumn时,需要传递一个用于将要使用的columnName的字符串。为什么它不允许像大多数其他HTML助手一样传递lambda表达式?我如何传递一个简单的lambda表达式以获取智能感知提示、保留类型安全性,并防止由于拼写错误而导致的运行时错误?对于列名,WebGrid不应该能够使用元数据显示属性来默认设置标题和格式吗?

我们常常使用以下内容:

@{ var grid = new WebGrid(model) }

@grid.GetHtml(
    // 这里设置表格样式格式...

    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))
    )
)
英文:

When creating a WebGridColumn, it requires passing in a string for the columnName that will be used. Why doesn't it allow passing a lambda expression like most other HTML Helpers? How can I pass a simple lambda to gain intellisense hints, preserve type-safety, and prevent runtime errors due to typos? And with the column name, shouldn't WebGrid be able to use the metadata display attributes to set the header and format by default?

What we use on a regular basis:

@{ 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))
    )
)

答案1

得分: 2

这是同事向我提出的问题。我们广泛使用WebGrid,并且不得不为每一列重复输入相同的信息,这真的非常令人沮丧。我研究了扩展WebGrid本身,但那似乎需要大量工作才能使其通用和类型安全。相反,我编写了一个HtmlHelper扩展,它返回一个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),
)

这真的很棒...

它使用内置的Expression功能来获取要传递给WebGridColumn构造函数的完整属性名称,并从MVC中获取已经内置的元数据以获取要在标题中显示的名称,并检查格式。

它还接受标题和格式值的覆盖,因此您可以自己设置它们;您可能希望为多个页面使用一个Person对象,但对某些页面调用Birthdate字段为“Birthday”,而对其他页面调用它为“Date of Birth”(我们尽量避免这样做,但有时这是终端用户的要求),或仅显示日期和月份:

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

This was the question posed to me by a colleague. We use WebGrid extensively, and having to repeatedly type the same info in duplicate or triplicate for every column was... frustrating to say the least. I looked into extending WebGrid itself, but that seemed like it would take a loooot of work to make generic and type-safe. Instead, I wrote an HtmlHelper extension that returns a WebGridColumn.

Code

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

Anywhere you would normally use WebGrid.Column() instead use 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),
)

That is beautiful...

What's going on?

It uses built-in Expression functionality to get the full property name to pass to the WebGridColumn constructor, and gets the already built-in metadata from MVC to get the name to display for headers, and check for formatting.

It also accepts overrides for the header and format values, so you can set them yourself; you may want one Person object for multiple pages, but some to call the Birthdate field "Birthday", and maybe others to call it "Date of Birth" (we try to avoid that, but sometimes it's an end user requirement), or show only the day and month:

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

huangapple
  • 本文由 发表于 2020年1月7日 01:10:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/59616245.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定