【问题标题】:.NET - Attaching an entity of type failed because another entity of the same type already has the same primary key value.NET - 附加类型的实体失败,因为相同类型的另一个实体已经具有相同的主键值
【发布时间】:2018-02-24 22:08:32
【问题描述】:

我知道有几个关于同一件事的问题,但似乎没有一个对我有帮助。我正在尝试执行 .RemoveRange() 并且我看到的每个问题都与编辑和添加有关。

以下是引发异常的方法的相关位:

public bool UpdateFileboundApplications(IList<IFileboundApplicationDm> fileboundApplications)
    {
        // get all mappings in the DB that match the incoming fileboundApplications
        var incomingFbAppsAlreadyExistingInDb =
            fileboundApplications.Where(app => app.Id == Db.inf_DMS_FBApplicationProjectMapping.SingleOrDefault(a => a.ApplicationId == app.Id)?.ApplicationId
                                               && app.FileboundProject != null).ToList();

        // in the case that application/project mappings include filebound applications with no project mapping,
        // pass the collection to a method which will handle removal of these records.
        var fbAppMappingsWithoutNulls = RemoveNullFileboundApplicationMappings(incomingFbAppsAlreadyExistingInDb, fileboundApplications);
        var fbAppMappingsAppIdsAndProjectIds = fbAppMappingsWithoutNulls.Select(x => new { appId = x.Id, projectId = x.FileboundProject.Id}).ToList();
        var dbRecords = Db.inf_DMS_FBApplicationProjectMapping.Select(y => new { appId = y.ApplicationId, projectId = y.ProjectID}).ToList();

        var fbApplicationDifferences =
            dbRecords.FindDifferences(fbAppMappingsAppIdsAndProjectIds,
            s => new Tuple<int, int>(s.appId, s.projectId),
            d => new Tuple<int, int>(d.appId, d.projectId));

        if (fbApplicationDifferences.ExistOnlyInSource.Any())
        {
            // items to remove from the table, as these apps are now assigned to a different project.
            var allAppsToRemove = fbApplicationDifferences.ExistOnlyInSource.Select(x => new inf_DMS_FBApplicationProjectMapping
                                                                                         {
                                                                                             ApplicationId = x.appId,
                                                                                             ProjectID = x.projectId,
                                                                                             MapId = Db.inf_DMS_FBApplicationProjectMapping.Single(m => m.ApplicationId == x.appId).MapId
                                                                                         }).ToList();

            Db.inf_DMS_FBApplicationProjectMapping.RemoveRange(allAppsToRemove);

        }

        Db.SaveChanges();
        return true;
    }

FWIW,我还将包含 RemoveNullFileboundApplicationMappings 的代码:

        private IEnumerable<IFileboundApplicationDm> RemoveNullFileboundApplicationMappings(IEnumerable<IFileboundApplicationDm> incomingFbAppsAlreadyExistingInDb,
                                                        IEnumerable<IFileboundApplicationDm> fileboundApplications)
    {
        // hold a collection of incoming fileboundApplication IDs for apps that have no associated fileboundProject
        var appIdsWithNoFbProject = fileboundApplications.Except(incomingFbAppsAlreadyExistingInDb)
                                                         .Select(app => app.Id);

        // get records in the table that now need to be removed
        var dbRecordsWithMatchingIds = Db.inf_DMS_FBApplicationProjectMapping.Where(mapping => appIdsWithNoFbProject.Contains(mapping.ApplicationId));

        if (dbRecordsWithMatchingIds.Any())
        {
            // remove records for apps that no will no longer have an associated Filebound project
            Db.inf_DMS_FBApplicationProjectMapping.RemoveRange(dbRecordsWithMatchingIds);
            Db.SaveChanges();
        }

        return fileboundApplications.Where(app => app.FileboundProject != null);
    }

最后,这里是 inf_DMS_FBApplicationProjectMapping 类:

    public partial class inf_DMS_FBApplicationProjectMapping
{
    public int MapId { get; set; } // <-- this is the PK
    public int ApplicationId { get; set; } 
    public int ProjectID { get; set; }
    public Nullable<int> Modified_By { get; set; }
    public Nullable<System.DateTime> Modified_On { get; set; }

    public virtual glb_Applications glb_Applications { get; set; }
}

}

异常如下:

{“附加类型为‘xxxx’的实体失败,因为同一类型的另一个实体已经具有相同的主键值。这可能在使用‘附加’方法或将实体的状态设置为‘未更改’时发生如果图中的任何实体具有冲突的键值,则为“已修改”。这可能是因为某些实体是新实体并且尚未收到数据库生成的键值。 在这种情况下,使用 'Add' 方法或 'Added' 实体状态来跟踪图,然后将非新实体的状态设置为 'Unchanged' 或 'Modified' 视情况而定。"}

我不太明白我需要如何使用 Db.inf_.....Add(),因为我不打算将记录添加到表中;我需要删除记录。

我不明白这个“附加到上下文”是什么意思以及它的真正含义。

我非常感谢社区对此的任何见解。一直在努力寻找解决这个问题的方法。谢谢!

【问题讨论】:

    标签: c# asp.net linq entity-framework-6


    【解决方案1】:

    我猜问题出在new 中,您使用它来组成您作为参数传递给RemoveRange 的列表。由于没有直接从您的DbSet 查询该列表中的实体,因此它们从未附加到您的本地上下文中,因此 EF 会感到困惑。

    您需要了解附加到上下文的实体的概念。 Entity Framework 跟踪对您正在使用的实体所做的更改,以便能够决定在您执行SaveChanges 时要做什么:插入、更新、删除。只有实体附加到上下文时,EF 才能做到这一点。这意味着他们有一个属性State,其值为AddedDeletedModifiedUnchanged等。

    在简单的场景中,这对您来说是透明的,因为当您执行 DbSet.Add(entity)DbSet.Find(entityId) 或作为查询结果获得实体实例时,实体会自动附加,例如 DbSet.Where(...)DbSet.FirstOrDefault(...)等。这就是为什么您之前在 EF 代码中可能永远不必担心附加实体的原因。

    在更复杂的场景中,例如您当前的场景,您尝试删除的实体尚未从这些操作之一中实例化,因此它们不会自动附加到您的上下文中。如果你用new 实例化它们,你必须明确地这样做。

    所以你应该在SaveChanges之前做这样的事情:

    foreach(var item in allAppsToRemove)
    {
        Db.Entry(item).State = EntityState.Deleted;
    }
    

    通过使用Entry 方法,实体被附加到上下文中,然后您将它们的状态显式设置为Deleted,以便在稍后执行SaveChanges 时删除它们。

    看看this page。即使它主要处理添加和更新案例,它也包含与您的删除问题相关的信息。在使用 EF 进行编程时,了解附加到本地 DbContext 的实体的概念将对您有很大帮助。在某些情况下,如果您不知道附加实体是如何工作的,您会遇到麻烦(您最终也会遇到一些“孤儿”错误)。

    注意:在 Entity Framework Core (EF7) 中有一个 AttachRange 方法可以在 RemoveRange 之前使用。

    【讨论】:

    • 感谢您的帮助。您建议的页面很有帮助;我开始更好地理解发生了什么。不幸的是,您建议的解决方案仍然抛出我的问题中描述的异常。就像您建议的那样,我正在遍历 allAppsToRemove 中的每个项目。异常被抛出 Db.Entry(item).State = EntityState.Deleted;
    • 也许问题是您使用new 实例化的项目不完全等于数据库中的项目(您没有填写所有属性:Modified_ByModified_On 是丢失),因此Db.Entry 将它们标识为不同的寄存器。尝试填充这些属性或在实体类中定义显式 Equals()。
    • 确定正在发生的事情;尝试一个临时的事情:在foreach 循环内,在Db.Entry 之前的行中,使用当前项目中的键值执行Find,并使用返回的var 而不是item var 作为@987654352 @ 范围。如果您没有收到“已附加”异常,则意味着 EF 不知道您编写的 item 由于某种原因(可能缺少额外的属性)与数据库中已经存在的相同。
    • 我认为您正在做某事.. 我尝试使用 .Find() 提出您的建议。这一次,抛出了一个不同的异常,它是在我的.RemoveRange() 调用中抛出的。异常消息:“无法删除该对象,因为它在 ObjectStateManager 中找不到。”
    • 尝试将使用Find 获得的元素添加到一个集合中,并将该集合用作RemoveRange 中的参数,而不是allAppsToRemove。它有效,那么问题出在您的投影中(在new 中)。
    【解决方案2】:

    Diana's 的帮助下,我能够解决这个问题。

    问题是我手动翻转实体状态并调用.RemoveRange()。我只需要翻转实体状态。以下是解决问题的相关位:

    ...
    ...
    ...
     if (fbApplicationDifferences.ExistOnlyInSource.Any())
                {
                    // items to remove from the table, as these apps are now assigned to a different project.
                    var allAppsToRemove = fbApplicationDifferences.ExistOnlyInSource.Select(x => new inf_DMS_FBApplicationProjectMapping
                                                                                                 {
                                                                                                     ApplicationId = x.appId,
                                                                                                     ProjectID = x.projectId,
                                                                                                     MapId = Db.inf_DMS_FBApplicationProjectMapping.Single(m => m.ApplicationId == x.appId).MapId
                                                                                                 }).ToList();
    
                    foreach (var app in allAppsToRemove)
                    {
                        var item = Db.inf_DMS_FBApplicationProjectMapping.Find(app.MapId);
                        Db.Entry(item).State = EntityState.Deleted;
                    }
    
                    //Db.inf_DMS_FBApplicationProjectMapping.RemoveRange(allAppsToRemove); <-- these items are already "flagged for deletion" with .State property change a few lines above. 
                }
    

    【讨论】:

      猜你喜欢
      • 2015-01-08
      • 2014-07-18
      • 1970-01-01
      • 1970-01-01
      • 2017-05-13
      • 1970-01-01
      • 1970-01-01
      • 2014-06-05
      • 2017-08-10
      相关资源
      最近更新 更多