繁体   English   中英

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

[英]Edit many to many relationship EntityFramework C# correctly

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

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

我的模型设置如下:

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

你可以看到一个Post可以属于很多Category ,一个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);
}

问题:

一切正常:我可以更新PostTitleContent没问题。 Post.Categories未在数据库中更新。

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

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

如果我理解你的意图,你应该在你的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; }

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

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

您需要告知实体框架记录已更改,以便EF更新记录。

如果您知道所提供的类别已经存在(这是我假设“假设所有用户输入都正确”时所假设的那样),那么您可以使用以下内容:

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

我假设您将实施某种检查和平衡以确保在上述代码之前存在记录,但是如果您不想在这里遇到麻烦,那么该片段应该对您有用。 如果类别不存在,则可以添加“Else”块以添加类别。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM