实体框架:objectstatemanager中已存在具有相同密钥的对象

时间:2013-10-25 15:00:11

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

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

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

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)

2 个答案:

答案 0 :(得分:6)

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

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

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

这将确保妥善处理背景!

答案 1 :(得分:0)

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

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

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

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

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

相关问题