まず、実際の Book および Author オブジェクトの定義:
public class Book
{
[Key]
public virtual int BookID { get; set; }
[Required]
[DataType(DataType.Text)]
[MaxLength(100)]
public virtual String Title { get; set; }
[DataType(DataType.MultilineText)]
public virtual String Abstract { get; set; }
[RegularExpression(@"\d{4}")]
public virtual String Year { get; set; }
[Required]
[Display(Name="Genre")]
public virtual int GenreID { get; set; }
public virtual Genre Genre { get; set; }
public virtual List<Author> Authors { get; set; }
public Book()
{
Authors = new List<Author>();
}
}
public class Genre
{
[Key]
public virtual int GenreID { get; set; }
[Required]
[DataType(DataType.Text)]
[MaxLength(30)]
public virtual String Name { get; set; }
public virtual List<Book> Books { get; set; }
}
public class Author
{
[Key]
public virtual int AuthorID { get; set; }
[Required]
[DataType(DataType.Text)]
[MaxLength(50)]
public virtual String Firstname { get; set; }
[Required]
[DataType(DataType.Text)]
[MaxLength(50)]
public virtual String Lastname { get; set; }
public virtual List<Book> Books { get; set; }
public Author()
{
Books = new List<Book>();
}
[NotMapped]
public string Fullname
{
get { return String.Format("{0}, {1}", Lastname, Firstname); }
}
public override string ToString()
{
return Fullname;
}
}
EF-Migrationsを使用して、データシードを入力したconfiguration.csファイルを取得し、add-migrationを使用して最初の移行を取得してデータベースを初期化します書籍と著者の間の多対多の関係を保持するための AuthorBooks。
これまでのところ、デモ アプリケーション全体が動作するようになりました。私が抱えている唯一の問題は、Entity Framework が AuthorBooks 関係の保存を拒否しているように見えることです。本の作成/編集時に、MultiSelectList-ListBox を介して関連する著者を選択できるように、アプリを設定しました。
BookControllerViewModel:
public class BookControllerViewModel
{
public Book actualBook { get; set; }
public String redirectUrl { get; set; }
public MultiSelectList Authors { get; set; }
public int[] AuthorIDs { get; set; }
public SelectList Genres { get; set; }
}
ViewModel には、作成/編集操作の対象となる本、本に基づく適切な選択情報を持つ Authors の MultiSelectList が含まれており、ユーザーが Create の ListBox で選択する選択された ID を保持するために int[] AuthorIDs を追加しました。 /ビューを編集します。
@model BookStoreInternet.ViewModels.BookControllerViewModel
<div class="editor-label">
@Html.LabelFor(model => model.actualBook.Title)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.actualBook.Title)
@Html.ValidationMessageFor(model => model.actualBook.Title)
</div>
<div class="editor-label">
<label>Author@( Model.actualBook.Authors.Count > 1 ? "s" :"" )</label>
</div>
<div class="editor-field">
@Html.ListBox("AuthorIDs", Model.Authors)
@Html.ValidationMessageFor(model => model.actualBook.Authors)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.actualBook.Abstract)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.actualBook.Abstract)
@Html.ValidationMessageFor(model => model.actualBook.Abstract)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.actualBook.Year)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.actualBook.Year)
@Html.ValidationMessageFor(model => model.actualBook.Year)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.actualBook.GenreID, "Genre")
</div>
<div class="editor-field">
@Html.DropDownList("actualBook.GenreID", Model.Genres)
@Html.ValidationMessageFor(model => model.actualBook.GenreID)
</div>
これも、コントローラー アクションにポストされる ViewModel という点でうまく機能します。
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(BookControllerViewModel bookVM)
{
if (ModelState.IsValid)
{
bookVM.actualBook.Authors = db.Authors.Where(x => bookVM.AuthorIDs.Contains(x.AuthorID)).ToList();
db.Entry(bookVM.actualBook).State = EntityState.Modified;
db.SaveChanges();
return Redirect(bookVM.redirectUrl);
}
bookVM.Authors = new MultiSelectList(db.Authors, "AuthorID", "Fullname", bookVM.actualBook.Authors.Select(x => x.AuthorID));
bookVM.Genres = new SelectList(db.Genres, "GenreID", "Name", bookVM.actualBook.GenreID);
return View(bookVM);
}
私が期待する値で適切に満たされています。ただし、機能しないのは、db.SaveChanges() が、本に追加した著者の保存を「黙って」拒否しているように見えることです。操作をデバッグすると、bookVM.ActualBook.Authors が実際に適切に設定され、選択された Authors が含まれていることがわかります...しかし、Jointable AuthorBooks に対応するエントリは作成されません...
なぜこれが機能しないのか、あるいはそれを機能させる方法さえ知っている人はいますか?
前もって感謝します!
Gertsの回答のおかげで、Controller-Actionを次のように変更することでこれが機能しました
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(BookControllerViewModel bookVM)
{
if (ModelState.IsValid)
{
var book = db.Books.Find(bookVM.actualBook.BookID);
book.Update(bookVM.actualBook);
book.Authors.Clear();
foreach(var aid in bookVM.AuthorIDs)
book.Authors.Add(db.Authors.Find(aid));
db.SaveChanges();
return Redirect(bookVM.redirectUrl);
}
bookVM.Authors = new MultiSelectList(db.Authors, "AuthorID", "Fullname", bookVM.actualBook.Authors.Select(x => x.AuthorID));
bookVM.Genres = new SelectList(db.Genres, "GenreID", "Name", bookVM.actualBook.GenreID);
return View(bookVM);
}
Book モデルにメソッドを追加する
internal void Update(Book book)
{
Title = book.Title;
Abstract = book.Abstract;
Year = book.Year;
GenreID = book.GenreID;
}
Gertに再び感謝します!