向对象List <entity> </entity>添加/删除相关实体

时间:2013-04-07 20:16:42

标签: c# razor asp.net-mvc-4 many-to-many entity-framework-5

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

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和last但并非最不重要的是,书籍能够保持书籍与作者之间的多对多关系。

到目前为止,我的整个演示应用程序正常运行。我唯一的问题是实体框架似乎拒绝保存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包含受创建/编辑操作影响的书籍,作者的MultiSelectList以及基于书籍的正确Selection-Info,我添加了int [] AuthorIDs来保存用户在ListBox中选择的所选ID创建/编辑视图。

@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>

这也适用于发布到控制器-Action

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

再次感谢格特!

1 个答案:

答案 0 :(得分:2)

由于Book-Author是多对多关联,因此您必须首先加载actualBook.Authors以便更改跟踪器能够注意到更改。然后,您可以添加/删除项目或将其替换为新的集合。不需要设置其状态。