2016-06-28 3 views
2

概要再利用可能なMVCコード:LINQの式で扱う値の型が

私は/編集を追加/ Entity Frameworkのオブジェクトのための機能を削除/基本的な検索を提供することを目的とMVCのセットを書いています。また、属性を使用して他のテーブルからドロップダウンをサポートするような、派手なこともいくつか行います。

プレイが主な目的はAdminField<T>のアレイ、Tでフィールド/プロパティを表すの各要素を含む継承汎用コントローラ(TがEFオブジェクトであるAdminController<T>)です。 AdminController<T>AdminViewModel<T>オブジェクトを生成できます。以下は、これらのオブジェクト間の関係のVisioチャートです。

enter image description here

問題

私の問題はビューです。値を表示するには、Html.EditorFor/HiddenFor/TextboxForヘルパーを使用しています。高度に単純化されたビューは、フィールドをループしてその値を表示し、編集可能なフィールドであれば編集を許可します。

@model AdminViewModel<ExampleEFObject> 
@using (Html.BeginForm()) 
{ 
    foreach (var f in Model.Fields) 
    { 
     var expression = Model.ExpressionFromField(f); 

     <label class="control-label col-md-3">@f.DisplayName</label> 

     @if (!f.Editable) 
     { 
      @Html.TextBoxFor(expression, new { @class = "form-control", @disabled = "disabled" }) 
     } 
     else 
     { 
      @Html.EditorFor(expression, new { htmlAttributes = new { @class = "form-control" } }) 
     } 
    } 
    <input type="submit" value="Save" class="btn btn-primary" /> 
} 

は、まあ、それは私がそれが見えるようにしたいものです:これは次のようになります。参照型であるフィールド/プロパティでうまく動作します。値型では、しかし、私はEditorForライン上System.InvalidOperationExceptionエラーを取得する:「テンプレートが唯一のフィールドへのアクセス、プロパティへのアクセス、単一次元配列のインデックス、または単一のパラメータのカスタムインデクサ式で使用することができます

をこれは、ViewModel内のTのインスタンスにアクセスする式を返すExpressionFromFieldメソッドの戻り値の型がExpression<Func<AdminViewModel<T>, object>>であるためです。オブジェクト型を返すので、式を生成するときにボクシングが強制されるので、(例えば)t => t.Countの代わりに、私のメソッドは実際にはt => Convert(t.Count)を返します。

現在の回避策

私の現在の回避策は、Expression<Func<AdminViewModel<T>, int>> IntExpressionFromField(AdminField<T> field)として定義される第2の方法を持つことです。 intを返しますので、式でボクシングを強制しません。しかし、それは私の意見は非常に醜います:

@model AdminViewModel<ExampleEFObject> 
@using (Html.BeginForm()) 
{ 
    foreach (var f in Model.Fields) 
    { 
     var expression = Model.ExpressionFromField(f); 
     var intExpression = Model.IntExpressionFromField(f); 

     <label class="control-label col-md-3">@f.DisplayName</label> 

     @if (!f.Editable) 
     { 
      if (intExpression == null) 
      { 
       @Html.TextBoxFor(expression, new { @class = "form-control", @disabled = "disabled" }) 
      } 
      else 
      { 
       @Html.TextBoxFor(intExpression, new { @class = "form-control", @disabled = "disabled" }) 
      } 
     } 
     else 
     { 

      if (intExpression == null) 
      { 
       @Html.EditorFor(expression, new { htmlAttributes = new { @class = "form-control" } }) 
      } 
      else 
      { 
       @Html.EditorFor(intExpression, new { htmlAttributes = new { @class = "form-control" } }) 
      } 
     } 
    } 
    <input type="submit" value="Save" class="btn btn-primary" /> 
} 

さらに悪いことに、これが唯一の1つの値の種類(int)をサポートしています。また、小数点、長整数型、その他のものをサポートしたいと思います。それぞれのカスタムメソッドを作成しなくても、他のタイプに対して保護するためのすべてのロジックを作成する必要はありません。

ヘルプ!

この問題を解決するには、エレガントで簡単な方法が必要です。存在しない場合、次のステップはHtmlヘルパーメソッドの使用を中止することだと思います。しかし、これはアプリケーションの他の部分と一貫性を失わせる必要があり、私はそれが問題を解決するだろうということを知らない。

ご協力いただければ幸いです。

+0

EFオブジェクトをViewに渡すのは悪い習慣です。おそらく、(DatePickerViewModel、TextBoxViewModelなどのエディタテンプレートを使用する)いくつかの抽象的なFieldViewModelの実装を含む抽象FormViewModelを作成し、コントローラでそのViewModelを変換してEntity Frameworkエンティティの操作を更新する必要があります。あなたがサービス層を持っているならDTOに。 –

+0

複雑なレイアウトや上手なユーザーインタラクションを行うことができないため、全体的に価値がないかもしれないので、多くのエンティティがない場合は、ViewModelsや手動コードに切り替えてエンティティを更新することが可能です。 –

+0

@raderick:ありがとう応答する。私はEFオブジェクトをビューに渡さず、一般的なViewModelのみを渡しました。しかし、それが悪い習慣であるなら、なぜ私が将来的により良いアプローチを選ぶことができるのか理解したい。 私がこれをやっているのは、何種類かの基本的なメンテナンス機能しか必要としないオブジェクトタイプが数多くあるからです。私は何十もの同じMVCセットを持つことを避けたい。 あなたが言ったアプローチは信じられないほど重く、制限しているようですが、私はフォールバックオプションとしてそれを覚えています。 – Daniel

答えて

0

既存のアプローチを使用してこの問題を解決できないため、私は最後に言及したアプローチを試みました:放棄HtmlHelperメソッド。これはすごくうまくいって、私はずっと前にそれをやりたかったと思う。

MVCのViewModelバインディングは、すなわちフィールドのcust => cust.Records.Address.Stateとして表現、私はSelectedItemがある文字列SelectedItem.Records.Address.Stateを(必要に応じて(私は修飾されていないプロパティ名を取得するために必要なので、手動で私のモデルにバインドするために、「名前」プロパティを使用して達成されます表示された値が含まれているのViewModelオブジェクト)。私のAdminField<T>オブジェクトがすでにMemberExpressionプロパティを持っていたので、私はちょうどToString()方法を使用し、パラメータを除去し、ViewModelにオブジェクト名を先頭に追加。醜い、私は知っているが、信頼性が高く、使いやすい。

前に、HtmlHelperオブジェクトにフィードする式が必要でしたが、今は値が必要なので、ExpressionFromField()メソッドを置き換えましたGetValue()メソッドを使用すると、stringの値が返されます。 stringを返すことが重要です。この変換により、ViewModelバインディングが編集された値の元のプロパティタイプへの変換を処理するため、すべての値タイプを読み書きすることができます。

私の新しいビューは次のようになります。

@model AdminViewModel<ExampleEFObject> 
@using (Html.BeginForm()) 
{ 
    foreach (var f in Model.Fields) 
    { 
     var propertyName = f.PropertyName(f); //unqualified, can contain multiple parts 
     var value = Model.GetValue(f);  //uses the field expression to retrieve the value 
               //of a "T SelectedItem" object in the ViewModel 
     <label class="control-label col-md-3">@f.DisplayName</label> 

     @if (!f.Editable) 
     { 
      <input class="form-control" disabled="disabled" name="@propertyName" type="text" value="@value" /> 
     } 
     else 
     { 
      <input class="form-control" name="@propertyName" type="text" value="@value" /> 
     } 
    } 
    <input type="submit" value="Save" class="btn btn-primary" /> 
} 

多くのクリーナーを!

私がこのアプローチで失うものは、検証です。ここからは、JavaScriptの検証を追加したり、テンプレートを作成してHtmlHelperが自動的に追加するデータ検証を複製することができます。いずれにしても、大したことはありません。

これは誰かを助けることを望みます。

注:Ivan Stoeve @などHtml.Editor/Html.Textboxを使用して、推奨プロパティ名ではなく、表現を経由して結合を受け入れています。これはまだビューに強く型付けされた式がないまま私を残しますが、それは私にそれを調整する価値があると思いますので、私は検証を返します。また、ドロップダウンオプションを追加するときにやらなければならない醜い文字列操作もクリアされます。 :)

+2

'Expression'の代わりに' string'を受け入れる 'HtmlHelper'メソッド(' TextBoxFor'の代わりに 'TextBox'など)を使うことはできませんか? –

+0

提案してくれてありがとう、私はそれを使うかもしれない。最終的に私のための学習は、拘束力のあるモデルであり、プロパティ名をビューに引き寄せました。 'HtmlHelper'を使う考えは、再利用可能なレイアウトを保持しながら強く型付けされた式を使うことでした。 まだ、これは少なくとも私に無料の検証を与えるでしょう。私はそれを取る。 – Daniel

関連する問題