我有以下类结构
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 的实例时, B 的两个不同 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 ,因为其中一个关系不会被插入。我不担心在一次提交中这样做。无论什么都有效。
答案 0 :(得分:0)
使用object和Equals运算符时出现问题:当在散列表或字典键中使用on对象时,非常重要的是覆盖GetHashCode方法。
EF使用字典作为内部上下文。
尝试覆盖类的GetHashCode,并且必须确保为同一个实例对象返回相同的结果。
修改强>
EF使用内部ObjectStateManager和几个字典:AddedEntityStore,ModifiedEntityStore,DeletedEntityStore,UnchangedEntityStore(Dictionary&lt;'EntityKey,EntityEntry&gt;)。 当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(键)强>
这与将GetHashCode与Dictionary和HashTable一起使用相关。 见Dino Esposito帖子:http://software2cents.wordpress.com/2014/04/12/hash-codes-and-equality-in-the-net-framework/
答案 1 :(得分:-1)
好的,经过一次非常彻底的调试和另一组眼睛,答案很简单:他们不相同的参考。当我最初发布这个时,我正在查看错误的属性!