EF4添加多个实体(ObjectStateManager中已存在具有相同键的对象)

SM.*_*SM. 3 entity-framework-4

我有一个具有很多属性的对象.一堆这些大对象将被插入到数据库中,但只有一个属性在更改.将要更改的属性不是主键.第一次SaveChanges成功,但后续的失败,"ObjectStateManager中已存在具有相同键的对象.....".这是代码中的流程:

    //create the entity and set the properties that don't change
    TheLargeObject obj = new TheLargeObject();
    obj.Prop1 = 
    obj.Prop2 = 
    ...
    obj.Prop20 = 

    //create a list of values that differ between each entity
    List<int> validIds = new List<int>();

    private static void SaveToDatabase(TheLargeObject obj, List<int> validIds)
    {

        foreach (int id in validIds)
        {
            //this is the only property that changes
            obj.KeyId = id;

            //make a copy - do we really need this?
            TheLargeObject newobj = new TheLargeObject();
            newobj = obj;

            using(Entities objContext = new Entities())
            {
                objContext.TheLargeObjects.AddObject(newobj); //ERROR: An object with the same key already exists in the ObjectStateManager.  
                objContext.SaveChanges();
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

我刚刚开始使用EF4,所以我可能会以错误的方式解决这个问题.谢谢

RPM*_*984 8

我不确定你在这里做什么.主要是这句话让我困惑:

一堆这些大对象将被插入到数据库中,但只有一个属性在更改.

一个怎样的新对象(即插入)是变化的?如果它是新的,有什么可以改变的?

模型中的所有实体都有一个EntityKey,它通常是数据库端的主键.

我认为你在做什么.AddObject,当你应该做的时候.Attach.

以下是INSERT新对象的方法:

var newFoo = new Foo();
ctx.Foos.AddObject(newFoo);
newFoo.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

以下是更新现有对象的方法:

var existingFoo = ctx.Foos.SingleOrDefault(x => x.Id == 1); // or however you want to get it
existingFoo.Name = "Changed foo";
newFoo.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

或者,如果您有一个分离的实体:

var existingFoo = new Foo();
existingFoo.Name = "Foo name";
ctx.Foos.Attach(existingFoo);
ctx.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

所以,我认为在您的示例中,您的代码应该只是:

objContext.TheLargeObjects.Attach(newObject);
objContext.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

简而言之,如果您有一个积极的实体已存在于数据库中,并且您已手动构建实体,请使用.Attach.如果您有品牌打击新对象,请使用.AddObject.

但是,为了安全起见 - 您可以再次获取对象,进行所需的更改,然后执行.SaveChanges().

.Attach通常用于无状态场景,例如网站,其中对象不会在请求之间保留在图形中,因此需要进行更改.Attach,或者在进行更改之前再次检索对象.

希望这能为你清除它.

  • 没问题.作为礼貌,请接受这个作为正确的答案 - 或者如果你不相信它解决了你的问题,那么发表你自己的答案.很高兴你把事情解决了.:) (2认同)