tl;dr: I have some database with many-to-many relationship. When I edit its data and call SaveChanges() nothing is being changed in database.
我有一些多对多关系的数据库。当我编辑它的数据并调用SaveChanges()时,在数据库中没有发生任何更改。
Full Question:
完整的问题:
I encountered really strange problem. I want to update many-to-many relationship but it doesn't want to change. My Models look like this (I've posted only this part that is important in this question):
我遇到了非常奇怪的问题。我想更新多对多关系,但它不希望改变。我的模型是这样的(我只贴了这个问题中很重要的部分):
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; }
[...]
}
What I do to update this db relationship:
我要做的是更新db关系:
Get current state of Note from database:
从数据库中获取注意的当前状态:
Note noteInDb = db.Notes.Include(x => x.NoteTags).ThenInclude(x => x.Tag).AsNoTracking().FirstOrDefault(x => x.NoteId == note.NoteId);
Clear List of NoteTags and add new one:
清除NoteTags列表,添加新标签:
noteInDb.NoteTags = new List<NoteTag>();
noteInDb = TagsToAddToTags(note);
What TagsToAddToTags does: it find proper Tags in database and adds them to List<> like that:
TagsToAddToTags的作用是:在数据库中找到合适的标签并将其添加到<>列表中:
note.NoteTags.Add(new NoteTag { Tag = tagInDb });
Then I say to database that I've modified some data and save it:
然后我对数据库说我修改了一些数据并保存下来:
db.Entry(noteInDb).State = EntityState.Modified;
db.SaveChanges();
noteInDb variable looks exactly what I want it to look when I'm saving it to database. Unfortunately nothing is being saved, when I view database it looks still the same as before.
noteInDb变量看起来和我保存到数据库时想要的完全一样。不幸的是,没有保存任何东西,当我查看数据库时,它看起来仍然和以前一样。
The weird fact is also that when I write this line just after SaveChanges():
奇怪的是,当我在SaveChanges()之后写这一行时:
Note noteInDb2 = db.Notes.Include(x=>x.NoteTags).ThenInclude(x=>x.Tag).FirstOrDefault(x => x.NoteId == note.NoteId);
I can see that noteInDb2 has 9 NoteTags (it has 4 of them before and I wanted it to have 5, so this is in fact sum of what was before and what I wanted to add). But when I go to LINQPad to see how it really looks like, it shows me 4 old NoteTags (no changes).
我可以看到,noteInDb2有9个NoteTags(之前有4个NoteTags,我希望它有5个noteInDb2,所以这实际上是之前的和我想要添加的总和)。但是当我去LINQPad查看它的样子时,它显示了4个旧的NoteTags(没有变化)。
What am I doing wrong?
我做错了什么?
2 个解决方案
#1
2
You can use this extension i made to remove the unselected and add the newly selected to the list
您可以使用我创建的这个扩展来删除未选中的,并将新选中的添加到列表中
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);
}
using it looks like this
使用它看起来像这样。
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
0
I think you need to move your code around. Since you retrieve data using AsNoTracking you changes are not tracked until you set the state of the entity as modified. So before you call TagsToAddToTags, call db.Entry(noteInDb).State = EntityState.Modified;
我认为你需要移动你的代码。由于使用AsNoTracking检索数据,所以在您将实体的状态设置为已修改之前,不会跟踪更改。因此,在调用TagsToAddToTags之前,调用db.Entry(noteInDb)。状态= EntityState.Modified;
#1
2
You can use this extension i made to remove the unselected and add the newly selected to the list
您可以使用我创建的这个扩展来删除未选中的,并将新选中的添加到列表中
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);
}
using it looks like this
使用它看起来像这样。
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
0
I think you need to move your code around. Since you retrieve data using AsNoTracking you changes are not tracked until you set the state of the entity as modified. So before you call TagsToAddToTags, call db.Entry(noteInDb).State = EntityState.Modified;
我认为你需要移动你的代码。由于使用AsNoTracking检索数据,所以在您将实体的状态设置为已修改之前,不会跟踪更改。因此,在调用TagsToAddToTags之前,调用db.Entry(noteInDb)。状态= EntityState.Modified;