具有IEnumerable <tmodel>表达式帮助程序</tmodel>的MVC页面

时间:2012-08-10 00:34:12

标签: c# generics expression-trees expression

我有一个MVC局部视图,它的模型是一个IEnumerable&lt; SomeModel&gt ;.

Html类型因此是HtmlHelper&lt; IEnumerable&lt; SomeModel&gt;&gt;
我们有一个自定义的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 Templating,脱离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中定义一个方法&lt; TModel&gt;能够理解ViewModel&lt; TModel&gt;的类实际上是一个 视图模型&LT; IEnumerable的&LT; TItem&GT;&GT;

<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&lt; TModel&gt;中定义这样的方法签名?可以强制执行TModel的类是IEnumerable&lt; TItem&gt;因此,允许表达式参数为Func&lt; TItem,TValue&gt;,或者我将不得不使用扩展方法。

0 个答案:

没有答案