【问题标题】:Entity Framework: An object with the same key already exists in the objectstatemanager实体框架:objectstatemanager中已经存在具有相同key的对象
【发布时间】:2013-10-25 15:00:10
【问题描述】:

我看到这个问题被问了很多,但是我还没有找到任何可以解决我遇到的问题的东西。

显然我正在使用实体框架来更新记录。但是,一旦更新完成,每当我尝试保存时,都会收到以下错误消息:

An object with the same key already exists in the objectstatemanager

起初,我从包含ZipCodeTerritory 模型对象zipToUpdate 的副本的视图中传递了一个集合对象。我通过拉出这个对象并只发送相关字段来更改代码。但是,我仍然遇到同样的错误。

奇怪的是我第一次运行这段代码时,它运行良好。之后的任何尝试都会出现错误。

控制器

这是调用编辑函数的方法中的代码

public static string DescriptionOnly(ZipCodeIndex updateZip)
{
    if (!string.IsNullOrWhiteSpace(updateZip.newEffectiveDate) || !string.IsNullOrWhiteSpace(updateZip.newEndDate))
    {
        return "Neither effective or end date can be present if updating Territory Code only; ";
    }

    _updated = 0;

    foreach (var zipCode in updateZip.displayForPaging.Where(x => x.Update))
    {
        ProcessAllChanges(zipCode, updateZip.newTerritory, updateZip.newStateCode, updateZip.newDescription, updateZip.newChannelCode);
    }

    _msg += _updated + " record(s) updated; ";

    return _msg;
}

这是实际进行更新的方法。

private static void ProcessAllChanges(ZipCodeTerritory zipToUpdate, string newTerritory, string newStateCode, string newDescription, string newChannelCode)
{
    try
    {
        if (!string.IsNullOrWhiteSpace(newTerritory)) zipToUpdate.IndDistrnId = newTerritory;
        if (!string.IsNullOrWhiteSpace(newStateCode)) zipToUpdate.StateCode = newStateCode;
        if (!string.IsNullOrWhiteSpace(newDescription)) zipToUpdate.DrmTerrDesc = newDescription;
        if (!string.IsNullOrWhiteSpace(newChannelCode)) zipToUpdate.ChannelCode = newChannelCode;
        if (zipToUpdate.EndDate == DateTime.MinValue) zipToUpdate.EndDate = DateTime.MaxValue;

        _db.Entry(zipToUpdate).State = EntityState.Modified;
        _db.SaveChanges();
        _updated++;
    }
    catch (DbEntityValidationException dbEx)
    {
        _msg += "Error during update; ";
        EventLog.WriteEntry("Monet", "Error during ProcessAllChanges: " + zipToUpdate.ToString() + " |EX| " + dbEx.Message);
    }
    catch (Exception ex)
    {
        _msg += "Error during update; ";
        EventLog.WriteEntry("Monet", "Error during ProcessAllChanges: " + zipToUpdate.ToString() + " |MESSAGE| " + ex.Message);
    }
}

编辑

ZipCodeIndex 对象包含ZipCodeTerritory 模型对象的列表。这些不是从 linq 查询中提取的,而是简单地从视图传递回控制器。这是启动进程的控制器方法的签名:

[HttpPost]
public ActionResult Update(ZipCodeIndex updateZip, string button)

【问题讨论】:

  • 您能否添加实例化 _db 的代码,它是(我认为)您的上下文......以及处理的位?我试图弄清楚您是否保持打开对象上下文而不是正确处理但无法从您的代码中看到?我建议使用 using 语句包装以确保正确处理。
  • @BenjaminPaul:就是这样,谢谢。完全由我监督,但由于这是一个静态类,我实例化了_db 的一个实例,当一个方法被调用为属性并因此一遍又一遍地使用它时。如果你想回复帖子,我会接受。再次感谢!
  • 非常感谢...我已经添加了我的答案!很高兴我能帮上忙!

标签: c# .net asp.net-mvc-3 entity-framework


【解决方案1】:

这是由于数据库上下文处理不当造成的,因为您从不处理上下文对象本身,您会遇到这类问题。

我建议将代码包装在 using 语句中..

using (var context = new MyContext())
{
   // Do my save code here...
}

这将确保正确处理上下文!

【讨论】:

    【解决方案2】:

    当我遇到这种类型的问题时——我还没有看到这个确切的问题,但有不少人喜欢它——这是因为要么我用一个上下文检索了我的实体,而我试图用另一个上下文来保存它们或者因为发生了某些事情导致 Entity Framework 失去对它们的跟踪 - 使用 GetAsNoTracking 将它们从数据存储中拉出会产生这种效果,尽管如果您知道您不会更改提供明显性能优势的实体.

    这样做的结果是 EF 认为您正在保存一个新实体,该新实体恰好具有它已经知道的实体的大部分相同属性,而不是对其熟悉的实体进行更改。

    从您的代码中,我无法告诉您确切的解决方案是什么,但我会通过密切关注您检索实体的位置并确保您通过相同的上下文保存更改来开始解决此问题.

    您的编辑表明您正在将新的ZipCodeTerritory 对象从您的视图向下传递到控制器。这些可能映射到您商店中现有的ZipCodeTerritory 条目,所以这里发生的情况是,因为它们没有从数据库中出来,但它们是正确类型的实体,数据上下文会看到它们作为新对象。它不知道您正在更新现有记录,因为它没有创建它们,因此它不包含对从视图返回的这些对象的跟踪引用。

    现在可能有一个更优雅的解决方案 - 我目前正在使用 4.5,所以我没有查看最近的 EF 行为 - 但一个简单的解决方案是从您的数据存储然后更新它们并将它们保存回来。在这种情况下,尽管您传回的对象属于ZipCodeTerritory 类型,但它们的行为更像是数据传输对象,这是造成混乱的部分原因——因为它们是由视图创建的(尽管来自起源于数据存储)您的数据上下文根本无法识别它们。

    【讨论】:

    • 刚刚根据您的回答发布了一个编辑。基本上我在创建对象时不会查询数据库。它们在视图模型对象的List 属性中被传递回控制器。
    猜你喜欢
    • 1970-01-01
    • 2014-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多