简体   繁体   English

无法跟踪实体类型的实例,因为已跟踪另一个具有与 {'Id'} 相同键值对的实例

[英]The instance of the entity type cannot be tracked because another instance with the same key value pair for{'Id'} is already being tracked

I know that this question has been asked several times and mostly people are directly suggesting solution to the code piece asked in the question but I really wanted to know the core reason behind this error as in due to which condition do we get this error?我知道这个问题已经被问过好几次,而且大多数人都直接建议解决问题中提出的代码片段,但我真的很想知道这个错误背后的核心原因,因为我们在什么情况下会得到这个错误? I have seen in a number of questions people telling different answers that could resolve the issue but still I haven't found the exact reason for this error.我在许多问题中看到人们给出了可以解决问题的不同答案,但我仍然没有找到导致此错误的确切原因。 I have read that it is due to the tracking behavior of EF Core, but what exactly in that behavior causes this issue?我读到这是由于 EF Core 的跟踪行为造成的,但究竟是什么行为导致了这个问题?

Some small code samples would be appreciated.一些小的代码示例将不胜感激。 thanks.谢谢。

The reason is, in essence, there in the error message..本质上,原因在错误消息中。

When you use EF to download something from a database then it will, unless you tell it not to, keep track of the entity it downloaded.当您使用 EF 从数据库下载某些内容时,除非您告诉它不要这样做,否则它会跟踪它下载的实体。 No matter what you do with the entity you got, EF will also remember it.无论你对你得到的实体做什么,EF 也会记住它。 It's not the sole reason but a highly visible reason is that it makes life much easier when it's time to save changes:这不是唯一的原因,但一个非常明显的原因是,当需要保存更改时,它会让生活变得更加轻松:

var u = db.Users.FirstOrDefault(x => x.Name == "John");
u.Name = "Jim";
db.SaveChanges();

If EF had literally just downloaded the data, made a User and handed it over and not kept any memory of it then the SaveChanges wouldn't work;如果 EF 从字面上只是下载了数据,创建了一个User并将其移交并且没有保留其中的任何 memory,那么SaveChanges将不起作用; it would have to look something like它必须看起来像

db.SaveChanges(u);

ie you'd have to give the altered data back.也就是说,您必须返还更改后的数据。 This would also pose more complications if you wanted to use optimistic concurrency because that generally works by comparing the in-db values with the originally-downloaded values to know whether someone else has edited the db in the time during which you've had the object.如果您想使用乐观并发,这也会带来更多的复杂性,因为这通常通过将 in-db 值与最初下载的值进行比较来了解其他人是否在您拥有 object 的时间编辑了数据库.

An optimistic save might look like:乐观的保存可能看起来像:

UPDATE user SET name = 'Jim' WHERE id = 123 and name = 'John'

The original name we knew the user to have ("John") is included in the update query.我们知道用户的原始名称(“John”)包含在更新查询中。 If no one else changed the user name then great, the update will succeed because the WHERE clause will be true.如果没有其他人更改用户名,那么很好,更新将成功,因为 WHERE 子句将为真。 If someone did rename this user before we did, then the update fails (updates 0 rows) and we can deal with it.如果有人在我们之前重命名了这个用户,那么更新失败(更新 0 行),我们可以处理它。

We couldn't do any of that that if EF lost all memory of what the old name was;如果 EF 丢失了旧名称的所有 memory,我们将无法执行任何操作; a User object just has a simple string property for Name that doesn't remember "John" before we overwrote it with "Jim" .. This emans that if EF had made a User, and just simply turned it over to you, it could never have known what the original name of the user was when it was time to update it back to the db User object 只有一个简单的Name字符串属性,在我们用"Jim"覆盖它之前不记得"John" 。当需要将其更新回数据库时,从来不知道用户的原始名称是什么

It's fair to say there's really quite a lot of smart stuff going in on the background of an EF context, far beyond what we see come out (ie "a User object") and remembering the objects it sees and data about them, their original values, current values, whether to add/update/delete them etc is vital to being able to function effectively可以公平地说,在 EF 上下文的背景中确实有很多聪明的东西,远远超出了我们看到的东西(即“用户对象”)并记住它看到的对象和关于它们的数据,它们的原始值、当前值、是否添加/更新/删除它们等对于能够有效地 function 至关重要


Where the problems come is if you do something like creating another whole new User entity with the same primary key as the one EF already knows, and try to attach it to EF's object store:问题来了,如果你做一些事情,比如用与 EF 已知的主键相同的主键创建另一个全新的User实体,并尝试将它附加到 EF 的 object 商店:

var u1 = db.Users.FirstOrDefault(x => x.Name == "John"); //EF now knows user 123
...
var u2 = new User { Id = 123, Name = "Mary"}
db.Users.Add(u2); //EF already knows user 123; John

EF won't be able to establish which record is authoritatively the truth of record if it knows about two users both with ID 123, so it refuses to start remembering another different user with an ID it already knows.如果 EF 知道 ID 为 123 的两个用户,则它无法确定哪个记录是权威的真实记录,因此它拒绝开始记住另一个具有它已知 ID 的不同用户。 In the code above the two users are different objects in different places in memory, and they share the same ID.上面代码中两个用户是memory中不同地方的不同对象,他们共享同一个ID。 This couldn't be mapped back to the DB这无法映射回数据库


It should probably also be pointed out that if you've told EF that the ID is generated by the database it won't matter at all what you put for the ID when you do the Add: EF will know it's an added entity that will have its ID overwritten by the db when it's saved so you won't get an "Another instance with the same id" error if your "temporary" conflicts with an existing one可能还应该指出的是,如果您已经告诉 EF ID 是由数据库生成的,那么在执行添加时为 ID 输入的内容根本无关紧要:EF 将知道它是一个添加的实体,它将保存时它的 ID 被数据库覆盖,这样如果您的“临时”与现有实例冲突,您就不会收到“另一个具有相同 ID 的实例”错误

Well, I have debugged this part of EF Core.好吧,我已经调试了这部分EF Core。 There are at least two checks in ChangeTracker for entity existence. ChangeTracker 中至少有两次检查实体是否存在。

  1. Find Entry by entity reference, if it is found - everything ok.通过实体引用查找条目,如果找到 - 一切正常。
  2. Find Entry by entity's Primary Key, if it is found - you have this exception.按实体的主键查找条目,如果找到 - 你有这个例外。

If second check is passed, attached entity is registered in both dictionaries (if you have several keys, it will be additional dictionaries to find entry by key).如果通过了第二次检查,则附加实体将在两个词典中注册(如果您有多个键,它将是额外的词典以通过键查找条目)。

It is simplified explanation, but it explains why better to look first at already tracked entities and then decide which Entry to use to avoid such exception.这是一个简化的解释,但它解释了为什么最好先查看已跟踪的实体,然后决定使用哪个条目来避免此类异常。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 无法跟踪实体类型“Entity”的实例,因为已跟踪另一个具有与 {'Id'} 相同键值的实例 - The instance of entity type 'Entity' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked 无法跟踪实体类型“书签”的实例,因为已经在跟踪具有相同键值 {'ID'} 的另一个实例 - The instance of entity type 'Bookmark' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked 无法跟踪实体类型“Item”的实例,因为已跟踪另一个具有与 {'Id'} 相同键值的实例 - The instance of entity type 'Item' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked 实体类型的实例 <T> 无法跟踪,因为已经跟踪了另一个具有相同的{&#39;Id&#39;}键值的实例 - The instance of entity type <T> cannot be tracked because another instance with the same key value for {'Id'} is already being tracked 无法跟踪实体类型 Model 的实例,因为已跟踪另一个具有相同 {&#39;Id&#39;} 键值的实例 - The instance of entity type Model cannot be tracked because another instance with the same key value for {'Id'} is already being tracked 无法跟踪实体类型“Bus”的实例,因为已经在跟踪具有相同键值 {'Id'} 的另一个实例 - The instance of entity type ‘Bus’ cannot be tracked because another instance with the same key value for {‘Id’} is already being tracked 无法跟踪实体类型“AppUser”的实例,因为已跟踪另一个具有与 {'Id'} 相同键值的实例 - The instance of entity type 'AppUser' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked 无法跟踪实体类型“IdentityUser”的实例,因为已跟踪另一个具有与 {'Id'} 相同键值的实例 - The instance of entity type 'IdentityUser' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked 无法跟踪实体类型“ Article”的实例,因为已经跟踪了另一个具有相同键值的{&#39;Id&#39;}实例 - The instance of entity type 'Article' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked 无法跟踪实体类型“用户”的实例,因为已经在跟踪另一个具有相同键值的“Id”实例 - The instance of entity type 'User' cannot be tracked because another instance with the same key value for 'Id' is already being tracked
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM