MVC5でICollectionをバインドする
EF用のモデルではナビゲーションプロパティをICollectionで実装する。これをforeachとEditorFor等で出力した場合のname属性の内容ではモデルバインダーが期待する動作を行ってくれない。すなわちコントローラーの引数にモデルを指定しても内容が空になってしまう。
(試行錯誤の内容を忘れないうちにメモしておく。これを書くにあたって改めて検証していないので、記憶違いや間違い等の可能性がある。)
方法1
foreachをforにしてElementAt()を使用する。ElementAt()の実装みてないけど、たぶんダメだよね。これ機能しないんだっけかな?
方法2
なんでもいいからIList型にしてforeachをforにする。
方法2-1
ICollection型からIList型に定義を変更する。これをやっていいなら苦労しない。
方法2-2
別途ViewModelを用意してIList型で皮をかぶせる。(面倒なので却下)
方法2-3
Viewで変数を用意してToList()でIList型に変換しforで回す。ポイントは変数名で、例えば
MODEL:
public virtual ICollection<Author> Authors { get; set; }
みたいなプロパティがあったとして
VIEW:
@{IList<Author> authors = Model.Authors.ToList();} @for (int i = 0; i < authors.Count; i++) { @Html.EditorFor(m => authors[i].Name) }
みたいな感じで変数名をプロパティ名と合わせておくと大文字小文字くらいはいいようにバインドしてくれた気がする。
【重要】
ここまで添え字がきちんと連続してないといけないのでajaxとかで動的にごにょごにょしようと思ったら無理がある。name属性は普通に用意されているヘルパーの範囲では書き換えられない模様。
方法3
BeginCollectionItemを使う。たぶんMs-PL。部分ビューやテンプレートに分けるやり方によっては方法1を組み合わせなきゃいけなくなって残念無念。式ツリーを自前でごにょごにょ出来ないか調査してみたいと思う。ブラウザで出力されたHTMLを見ると分かるがindexというキーワードを(モデルバインダーが)特別扱いしているらしい。
- MVC Series Part 1: Dynamically Adding Items Part 1 | //InterKnowlogy/ Blogs
- http://blogs.interknowlogy.com/2014/08/01/mvc-series-part-1-dynamically-adding-items-part-1/
- GitHub - danludwig/BeginCollectionItem: This Html Helper leverages the default model binder in ASP.NET MVC 2 and higher to materialize viewmodel collection properties from an HTTP POST.
- https://github.com/danludwig/BeginCollectionItem
方法4
ラムダ式1つだけを引数に受け取るEditorFor()を使用する(これMSDNのどこみたら分かるの?)。確認の仕方を間違えていて普通に添え字使用だったが、これと方法3を組み合わせる事によって方法1を排除できる!(下記は方法3を組み合わせていない例)
こんな感じ。Authorは例なので適宜読み替える事。
MODEL:
public class Hoge { public virtual ICollection<Author> Authors { get; set; } }
VIEW: プロジェクト名\Views\コントローラー名\EditorTemplates\Author.cshtml
@model プロジェクト名.Models.Author @Html.TextBoxFor(m => m.Name) @* 内容はお好みで *@
MAIN-VIEW: プロジェクト名\Views\コントローラー名\Create.cshtml
@model プロジェクト名.Models.Hoge @Html.EditorFor(m => m.Authors)
Authorをモデルとして受け取るテンプレートにIEnumrable
名前が templateName パラメーターと一致するテンプレートがコントローラーの EditorTemplates フォルダー内で見つかった場合、そのテンプレートを使用して式が表示されます。 コントローラーの EditorTemplates フォルダー内でテンプレートが見つからない場合は、Views\Shared\EditorTemplates フォルダーで、templateName パラメーターの名前と一致するテンプレートが検索されます。 テンプレートが見つからない場合は、既定のテンプレートが使用されます。
EditorExtensions.EditorFor Method (System.Web.Mvc.Html) | Microsoft Docs
このメソッドは、表示されるプロパティのデータ型に応じて、またプロパティが特定の属性でマークされているかどうかによって、異なる HTML マークアップを生成します。 このメソッドは、次の規則に従ってマークアップを表示します。
方法5以降
ここからが本番!というところで力尽きました。続きは書かない気がする。
- c# - Iterating ICollection from ViewModel within View - Stack Overflow
- http://stackoverflow.com/questions/29652823/iterating-icollection-from-viewmodel-within-view
- c# - Correct, idiomatic way to use custom editor templates with IEnumerable models in ASP.NET MVC - Stack Overflow
- http://stackoverflow.com/questions/25333332/correct-idiomatic-way-to-use-custom-editor-templates-with-ienumerable-models-in
- c# - EditorFor IEnumerable
with TemplateName - Stack Overflow - http://stackoverflow.com/questions/14038392/editorfor-ienumerablet-with-templatename
- [C#] public static class HtmlHelperExtensions { public static MvcHtmlString Edi - Pastebin.com
- http://pastebin.com/c5HcS0Pj