正确编辑多对多关系EntityFramework C#

时间:2018-05-07 13:11:23

标签: c# entity-framework

我正在尝试在EntityFramework CodeFirst中编辑数据透视表的内容。

我正在使用.NET Framework 4.6.1和EntityFramework 6.1.3。

我的模型设置如下:

Threads

正如您所看到的,public class Post { /// <summary> /// The post's id in the database /// </summary> public int Id { get; set; } /// <summary> /// The the categories the post belongs to /// </summary> [InverseProperty("Posts")] public virtual ICollection<Category> Categories { get; set; } /// <summary> /// The title of the post /// </summary> [Required] public string Title { get; set; } /// <summary> /// The content of the post /// </summary> [Required] public string Content { get; set; } /// <summary> /// The moment the post was made /// </summary> public DateTime? TimeStamp { get; set; } } public class Category { /// <summary> /// The id of the category in the database /// </summary> public int Id { get; set; } /// <summary> /// The posts that belong to this category /// </summary> [InverseProperty("Categories")] public virtual ICollection<Post> Posts { get; set; } /// <summary> /// The name of the category /// </summary> [Required] public string Name { get; set; } } 可以属于多个PostCategory可以有多个Category

当我尝试更新帖子并将其更改为Post时,会出现问题。

控制器:

Category

问题:

一切正常:我可以更新[HttpPut] [ResponseType(typeof(void))] public IHttpActionResult PutPost(int id, Post post) { if (id != post.Id) { return BadRequest(); } var entry = db.Entry(post); entry.State = EntityState.Modified; // Created at must not be updated. entry.Property(x => x.TimeStamp).IsModified = false; // Also attatch categories foreach (var category in post.Categories) // For the example, I assume all user input is correct. { db.Categories.Attatch(category); // This is not working } try { db.SaveChanges(); } catch (DbUpdateConcurrencyException) { if (!PostExists(id)) { return NotFound(); } else { throw; } } return StatusCode(HttpStatusCode.NoContent); } 的{​​{1}}和Title没问题。但是Content未在数据库中更新。

我见过人们通过首先从数据库中获取实体并使用Post然后编辑该对象内的所有内容来更新多对多关系。但是,这需要从数据库中加载大量数据,这些数据在之后被否定。这对我来说似乎是不好的做法。

问题: 如何正确更新EntityFramework中的多对多关系,而无需从数据库中加载不必要的数据?

2 个答案:

答案 0 :(得分:1)

如果我理解你的意图,你应该在你的DBContext中尝试这样的事情。它将创建一个名为PostsCategories的第三个表,您将在其中拥有Category和Post之间的关系。

 protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<Category>()
                       .HasMany(t => t.Posts)
                       .WithMany(t => t.Categories)
                       .Map(m =>
                       {
                           m.ToTable("PostsCategories");
                           m.MapLeftKey("CategoryId");
                           m.MapRightKey("PostId");
                       });
   }

编辑:

所以试试这个: 在DbContext中:

    public void UpdateManyToMany<TSingle, TMany>(
        TSingle localItem,
        Func<TSingle, ICollection<TMany>> collectionSelector,
        int[] collectionIds)
        where TSingle : class
        where TMany : class
    {
        if (collectionIds == null)
            collectionIds = new int[0];

        DbSet<TMany> manyItemDbSet = Set(typeof(TMany)).Cast<TMany>();
        ICollection<TMany> dbColl = collectionSelector(localItem);
        dbColl.Clear();
        foreach (var keyValue in collectionIds)
        {
            var entity = manyItemDbSet.Find(keyValue);
            dbColl.Add(entity);
        }
    }

然后在你的控制器

db.UpdateManyToMany(post, e => e.Categories, post.MultipleCategories);

其中post.MultipleCategories是类别的ID,因此您可能必须在Post模型中添加一些字段。

[NotMapped]
public int[] MultipleCategories{ get; set; }

答案 1 :(得分:1)

不确定你会得到你想要的东西。在任何情况下,您都必须将实体图加载到实体框架中,以便它可以跟踪更改。

如您的示例中所述,Attach(顺便拼写错误)只会将对象添加到Entity Framework的更改跟踪器中,默认状态为Unchanged。

您需要告诉Entity Framework该记录已更改,以便EF更新记录。

如果你知道所提供的类别已经存在(这就是我所说的那样,假设所有用户输入都是正确的&#34;)那么你可以使用以下内容:< / p>

foreach (var category in post.Categories)
{
    // This will attach the entity and set the entity state to modified
    // Note this will throw an exception if the underlying entity doesn't
    // exist when the "SaveChanges" method is called.
    context.Entry(category).State = EntityState.Modified;
}

我假设你要实施某种检查和平衡以确保在上述代码之前存在记录,但是如果你不想在这里遇到麻烦,那么应该是一个片段为你工作。一个&#34; Else&#34;可以添加块以添加类别,如果它也不存在。

foreach (var category in post.Categories)
{
    var existingRecord = context.Categories.SingleOrDefault(x => x.ID == category.Id);
    if (existingRecord != null)
    {
        context.Entry(existingRecord).CurrentValues.SetValues(category)
    } 
}