0

好的,首先我的实际书籍和作者对象定义:

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 文件,我在其中输入了我的 Data-Seed,然后使用 add-migration 进行第一次迁移以初始化数据库 EF 正确创建相应的表 Authors、Books、Genres 和最后但并非最不重要的AuthorBooks 保存书籍和作者之间的多对多关系。

到目前为止,我的整个演示应用程序都在工作。我唯一的问题是实体框架似乎拒绝保存 AuthorBooks 关系。我已经设置了我的应用程序,以便在书籍创建/编辑时我可以通过 MultiSelectList-ListBox 选择相关的作者。

书控制器视图模型:

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包含受创建/编辑操作约束的书籍,这是基于本书的正确选择INFO的多电机列表,我添加了INT []权威以保留用户将在Create listbox中选择的选择IDS /编辑视图。

@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 实际上设置正确并包含选定的作者......但未创建可连接 AuthorBooks 的相应条目......

有人知道为什么这不起作用,甚至可能如何使它起作用吗?

提前致谢!


Thanks to Gerts answers I got this working by modifying the Controller-Action to

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

and adding a method to the Book model

    internal void Update(Book book)
    {
        Title = book.Title;
        Abstract = book.Abstract;
        Year = book.Year;
        GenreID = book.GenreID;
    }

Thanks again to Gert!

4

1 回答 1

2

Because Book-Author is a many to many association you have to load actualBook.Authors first for the change tracker to be able to notice changes. Then you can add/remove items from it or replace it by a new collection. Setting its state is not necessary.

于 2013-04-07T20:38:48.293 回答