3

そのモデルが IEnumerable<SomeModel> であるという MVC 部分ビューがあります。

したがって、Html タイプは HtmlHelper<IEnumerable<SomeModel>> です
。ViewModel のインスタンスで公開するカスタム WebViewPage があります。

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

この背後にある基本的な前提は、C# ViewModel 定義から、Knockout や JQuery Templating などの Javascript テンプレート システムのクライアント側テンプレート/バインディング式を駆動することです。

テーブル ヘッダーの 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 であることを理解できる ViewModel<TModel> クラスでメソッドを定義したいので、最初の式で現在のモデルを渡して 2 番目の式をブートストラップする必要があるのは好きではありません<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