【问题标题】:How to update many-to-many relationship in Entity Framework 7?如何在 Entity Framework 7 中更新多对多关系?
【发布时间】:2016-02-13 11:14:48
【问题描述】:

tl;dr:我有一些具有多对多关系的数据库。当我编辑它的数据并调用SaveChanges() 时,数据库中没有任何变化。

完整问题:

我遇到了一个非常奇怪的问题。我想更新多对多关系,但它不想改变。

我的模型看起来像这样(我只发布了对这个问题很重要的部分):

public class Note
{
    public Note()
    {
        NoteTags = new HashSet<NoteTag>()
    }

    public int NoteId { get; set; }
    public virtual ICollection<NoteTag> NoteTags { get; set; }
    [...]
}

public class NoteTag
{
    public int NoteId { get; set; }
    public Note Note { get; set; }

    public int TagId { get; set; }
    public Tag Tag { get; set; }
}

public class Tag
{
    public Tag()
    {
        NoteTags = new HashSet<NoteTag>();
    }

    public int TagId { get; set; }
    public string Name { get; set; }
    public virtual ICollection<NoteTag> NoteTags { get; set; }
    [...]
}

我如何更新这个数据库关系:

从数据库中获取Note的当前状态:

Note noteInDb = db.Notes.Include(x => x.NoteTags).ThenInclude(x => x.Tag).AsNoTracking().FirstOrDefault(x => x.NoteId == note.NoteId);

清除List&lt;NoteTags&gt;并添加一个新的:

noteInDb.NoteTags = new List<NoteTag>();
noteInDb = TagsToAddToTags(note);

TagsToAddToTags 的作用:它在数据库中找到合适的Tags,并将它们添加到List&lt;&gt;,如下所示:

note.NoteTags.Add(new NoteTag { Tag = tagInDb });

然后我告诉数据库我修改了一些数据并保存:

db.Entry(noteInDb).State = EntityState.Modified;
db.SaveChanges();

noteInDb 变量看起来与我在将其保存到数据库时的样子完全一样。 不幸的是,没有保存任何内容,当我查看数据库时,它看起来仍然和以前一样。

奇怪的事实还在于,当我在 SaveChanges() 之后写下这一行时:

Note noteInDb2 = db.Notes.Include(x=>x.NoteTags).ThenInclude(x=>x.Tag).FirstOrDefault(x => x.NoteId == note.NoteId);

我可以看到 noteInDb2 有 9 个 NoteTags(它之前有 4 个,我希望它有 5 个,所以这实际上是之前的内容和我想要添加的内容的总和)。但是当我去 LINQPad 看看它真正的样子时,它显示了 4 个旧的 NoteTags(没有变化)。

我做错了什么?

【问题讨论】:

    标签: asp.net entity-framework entity-framework-core


    【解决方案1】:

    您可以使用我制作的这个扩展来删除未选择的项目并将新选择的项目添加到列表中:

    public static void TryUpdateManyToMany<T, TKey>(this DbContext db, IEnumerable<T> currentItems, IEnumerable<T> newItems, Func<T, TKey> getKey) where T : class
    {
        db.Set<T>().RemoveRange(currentItems.Except(newItems, getKey));
        db.Set<T>().AddRange(newItems.Except(currentItems, getKey));
    }
    
    public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other, Func<T, TKey> getKeyFunc)
    {
        return items
            .GroupJoin(other, getKeyFunc, getKeyFunc, (item, tempItems) => new { item, tempItems })
            .SelectMany(t => t.tempItems.DefaultIfEmpty(), (t, temp) => new { t, temp })
            .Where(t => ReferenceEquals(null, t.temp) || t.temp.Equals(default(T)))
            .Select(t => t.t.item);
    }
    

    使用它看起来像这样:

    var model = db.Notes
        .Include(x => x.NoteTags)
        .FirstOrDefault(x => x.NoteId == note.NoteId);
    
    db.TryUpdateManyToMany(model.NoteTags, listOfNewTagIds
    .Select(x => new NoteTag
    {
        TagId = x,
        NoteId = note.NoteId
    }), x => x.TagId);
    

    【讨论】:

      【解决方案2】:

      我认为您需要移动代码。由于您使用 AsNoTracking 检索数据,因此在您将实体的状态设置为已修改之前,您不会跟踪更改。所以在调用 TagsToAddToTags 之前,调用 db.Entry(noteInDb).State = EntityState.Modified;

      【讨论】:

      • 好吧,还是不行。当我调用 SaveChanges 时,我能否以某种方式检查实体框架将更改哪些数据? EF 是否将正在对数据库执行的查询保存在某个地方?这会很有帮助,因为当我调用 SaveChanges() 时,我不知道 EF 到底在做什么
      • 您可以通过添加跟踪提供程序来跟踪查询,但您也可以在 SQL Server 探查器中轻松跟踪它们。您可以在添加实体后检查它们的状态,这可能是最好的起点。 db.Entry(newNoteTag).State 在您触发保存更改之前。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-01-28
      • 2016-10-05
      • 1970-01-01
      • 2019-06-18
      • 2021-07-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多