3

我有一个 MVC 部分视图,它的模型是 IEnumerable<SomeModel>。

因此,Html 类型是 HtmlHelper<IEnumerable<SomeModel>>
我们有一个自定义 WebViewPage,它在 ViewModel 的实例中公开

public class SomeModel
{
    [Display("Model Id")]
    public int Id { get; set; }

    [Display("The Name")]
    public string Name { get; set; }
}

public abstract class ViewModelWebViewPage : WebViewPage { }
public abstract class ViewModelWebViewPage<TModel> : ViewModelWebViewPage
{
    ... 
    public ViewModel<TModel> { get; set; }
}

这背后的基本前提是驱动 Javascript 模板系统(如 Knockout 或 JQuery 模板)的客户端模板/绑定表达式脱离 C# ViewModel 定义

我正在编写一些方法来帮助获取表头的 DisplayName(基本上是 Display 属性的值 - 我们的真实版本涉及使用子类 DataAnnotationsMetadataProvider 和 IMetadataAware 属性进行本地化)

public class ViewModel<TModel>
{
    public MvcHtmlString DisplayNameFor<TProperty>( Expression<Func<TModel, TProperty>> expression )
    {
        ModelMetadata metadata = ModelMetadata.FromLambdaExpression( expression, this.html.ViewData );
        return new MvcHtmlString(metadata.DisplayName);
    }

    public MvcHtmlString DisplayNameFor<TItem, TProperty>( Expression<Func<TModel, IEnumerable<TItem>>> expression, Expression<Func<TItem, TProperty>> itemExpression )
    {
        ModelMetadata metadata = ModelMetadata.FromLambdaExpression( itemExpression, new ViewDataDictionary<TItem>() );
        return new MvcHtmlString(metadata.DisplayName);
    }
}

在视图中,我会使用这个助手,比如......

@model IEnumerable<SomeModel>
<table>
  <thead>
    <tr>
      <!-- 
          "this" is a ViewModelViewWebPage<IEnumerable<SomeModel>>
          ViewModel is a ViewModel<IEnumerable<SomeModel>>
          m is an IEnumerable<SomeModel>, 
          item is of type SomeModel 
      -->
      <th>@ViewModel.DisplayNameFor( m => m, item => item.Id )</th>
      <th>@ViewModel.DisplayNameFor( m => m, item => item.Name )</th>
    </tr>
  </thead>
  ... remainder of table html
</table>

这一切都有效,我在呈现的 HTML 中得到了预期的结果

<table>
  <thead>
    <tr>
      <th>Model Id</th>
      <th>The Name</th>
    </tr>
  </thead>
  ... remainder of table html
</table>

我不喜欢我必须在第一个表达式中传递当前模型来引导第二个,因为我想在 ViewModel<TModel> 类中定义一个能够理解 ViewModel<TModel> 实际上是 ViewModel 的方法<IEnumerable<TItem>>

<th>@ViewModel.DisplayNameFor( m => m.Id )</th>

我设法创建了一个扩展方法

    public static MvcHtmlString DisplayNameFor<TItem, TValue>(this ViewModel<IEnumerable<TItem>> viewModel, Expression<Func<TItem, TValue>> expression)
    {
        ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, new ViewDataDictionary<TItem>());

        return new MvcHtmlString(metadata.DisplayName);
    }

但我希望它是 ViewModel 类本身的一部分,但我似乎可以找到一种定义 DisplayNameFor 方法来推断 IEnumerable 关系的方法。

我试过了

    public MvcHtmlString DisplayNameFor<TItem, TValue>(Expression<Func<TItem, TValue>> expression)
        where TModel : IEnumerable<TItem>
    {
        ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, new ViewDataDictionary<TItem>());

        return new MvcHtmlString(metadata.DisplayName);
    }

但这不会编译,因为来自约束的 TModel 不属于封闭类型,但它应该作为方法的 Type 参数。

是否可以在 ViewModel<TModel> 类中定义这样的方法签名,它可以强制 TModel 是 IEnumerable<TItem> 关系,因此允许表达式参数是 Func<TItem, TValue>,或者我将拥有使用扩展方法。

4

0 回答 0