我有一个 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>,或者我将拥有使用扩展方法。