我有以下类结构
class A { long Id { get; set; } ICollection<B> ManyB { get; set; } }
class B { long Id { get; set; } C { get; set; } }
class C { long Id { get; set; } Name { get; set; } }
用一个看起来像这样的实例图
当我添加一个 A 的实例时,它在 ManyB 中有两个不同的 B 实例引用相同的 C 实例,当我调用此代码时,EF 尝试插入两次 C:
var a = CreateItAsDescribedAbove();
context.Add(a)
context.SaveChanges();
上面的所有对象都是新的;也就是说,它们不是数据库中已有的实体。当我将代码更改为以下内容时,我仍然有相同的行为:
var a = CreateItAsDescribedAbove();
A.ManyB
.Select(b => b.C)
.DistinctBy(c => c.Id)
.ForEach(c => context.Add(c));
context.SaveChanges();
context.Add(a)
A.ManyB
.Select(b => b.C)
.ForEach(c => context.Entry(c).State = EntityState.Unchanged);
context.SaveChanges();
我本以为在先前的添加之后强制 EF 将对象视为未更改应该可行,但它们似乎被忽略了。
我如何说服 EF 保存我的 C 实例一次?请注意,我不能简单地取消其中一个 B.C,因为这样就不会插入其中一个关系。我不关心在一次提交中执行此操作。什么都管用。
最佳答案
使用对象和等于运算符时存在一个问题:当在哈希表或字典键中使用对象时,覆盖 GetHashCode 方法非常重要。
EF 使用字典作为内部上下文。
尝试为您的类覆盖 GetHashCode,并且您必须确保为相同的实例对象返回相同的结果。
编辑:
EF 使用带有多个字典的内部 ObjectStateManager:AddedEntityStore、ModifiedEntityStore、DeletedEntityStore、UnchangedEntityStore (Dictionary<'EntityKey, EntityEntry>)。 当 EF 在“SaveChanges”上搜索这些字典中的实体时,使用字典的 TryGetValue 方法。 因此,为简洁起见,这是代码:
private int FindEntry(TKey key)
{
if ((object) key == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
if (this.buckets != null)
{
int num = this.comparer.GetHashCode(key) & int.MaxValue;
for (int index = this.buckets[num % this.buckets.Length]; index >= 0; index = this.entries[index].next)
{
if (this.entries[index].hashCode == num && this.comparer.Equals(this.entries[index].key, key))
return index;
}
}
return -1;
}
我们所有人都可以看到这句话: this.comparer.GetHashCode(key)
这与将 GetHashCode 与 Dictionary 和 HashTable 一起使用一样相关。 请参阅 Dino Esposito 帖子:http://software2cents.wordpress.com/2014/04/12/hash-codes-and-equality-in-the-net-framework/
关于c# - 为什么 EF 尝试使用单个或多个 SaveChanges() 插入实例两次?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37119851/