为什么EF尝试使用单个或多个SaveChanges()插入实例两次?

时间:2016-05-09 15:38:36

标签: c# entity-framework entity-framework-6

我有以下类结构

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

使用看起来像这样的实例图

object graph

当我添加 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 ,因为其中一个关系不会被插入。我不担心在一次提交中这样做。无论什么都有效。

2 个答案:

答案 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)

好的,经过一次非常彻底的调试和另一组眼睛,答案很简单:他们相同的参考。当我最初发布这个时,我正在查看错误的属性!