我知道有几个问题提出了同样的事情,但似乎没有一个对我有所帮助。我正在尝试做一个.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'的实体失败,因为同一类型的另一个实体已经具有相同的主键值。当使用'Attach'方法或将实体的状态设置为'Unchanged'时,可能会发生这种情况。如果图中的任何实体具有冲突的键值,则为“已修改”。这可能是因为某些实体是新的并且尚未接收数据库生成的键值。 在这种情况下,使用“添加”方法或“已添加”实体状态来跟踪图表,然后根据需要将非新实体的状态设置为“未更改”或“已修改”。“}
我不太明白我需要如何使用Db.inf _..... Add(),因为我不打算在表中添加记录;我需要删除记录。
我不明白这种“附加到背景”是什么以及它的真正含义是什么。
我非常感谢社区对此有任何见解。试图找到解决这个问题的方法一直很困难。谢谢!
答案 0 :(得分:1)
我想问题出现在new
,您用来将作为参数传递的列表组成RemoveRange
。由于该列表中的实体尚未直接从您的DbSet
查询,因此它们从未被附加到您的本地环境中,因此EF会感到困惑。
您需要了解附加到上下文的实体的概念。实体框架会跟踪对您正在使用的实体所做的更改,以便能够在执行SaveChanges
时决定要执行的操作:插入,更新,删除。如果实体附加到上下文,EF只能执行此操作。这意味着他们的属性State
的值为Added
,Deleted
,Modified
,Unchanged
等。
在简单的场景中,这对您来说是透明的,因为当您执行DbSet.Add(entity)
,DbSet.Find(entityId)
或者作为查询结果获得实体实例时,实体会自动附加,例如{{1 }},DbSet.Where(...)
等。这就是为什么你可能永远不必担心EF代码中的附加实体。
在更复杂的场景(如当前场景)中,您尝试删除的实体尚未从其中一个操作中实例化,因此它们尚未自动附加到您的上下文中。如果使用DbSet.FirstOrDefault(...)
实例化它们,则必须明确地执行此操作。
所以你应该在new
:
SaveChanges
通过使用方法foreach(var item in allAppsToRemove)
{
Db.Entry(item).State = EntityState.Deleted;
}
,实体将附加到上下文,然后您明确地将其状态设置为Entry
,以便在以后执行Deleted
时删除它们。
看看this page。即使它主要处理添加和更新案例,它也包含与删除问题相关的信息。在使用EF进行编程时,了解附加到本地SaveChanges
的实体的概念将对您有所帮助。在某些情况下,如果您不知道附加实体是如何工作的,那么您将遇到麻烦(您最终也会遇到一些'孤儿'错误)。
注意:在Entity Framework Core(EF7)中,可以在DbContext
之前使用AttachRange
方法。
答案 1 :(得分:0)
在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.
}