2

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

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

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)
4

2 回答 2

6

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

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

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

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

于 2013-10-25T15:31:29.513 回答
0

当我遇到这种类型的问题时——我还没有看到这个确切的问题,但很多人喜欢它——这是因为我已经用一个上下文检索了我的实体并且我试图使用另一个上下文来保存它们,或者因为某些东西碰巧导致实体框架失去对它们的跟踪 - 将它们从数据存储中拉出GetAsNoTracking会产生这种效果,尽管如果您知道您不会更改提供明显性能优势的实体。

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

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

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

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

于 2013-10-25T15:06:48.803 回答