C# 实体框架创建与现有实体有关系的新实体,导致尝试创建现有实体的新副本
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8759699/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Entity Framework creating new entity with relationship to existing entity, results in attempt to create new copy of the existing entity
提问by Amanda Kitson
I am trying to create a new user object with a specific Role. The "Role" is an existing entity in EF. I have googled, and stackoverflowed until I am blue in the face, and I have tried all the stuff that seems to be working for everyone else. But when I try to save my new user object, it first tries to create a new "Role", instead of just creating the new user object with a reference to the existing Role.
我正在尝试创建一个具有特定角色的新用户对象。“角色”是 EF 中的现有实体。我用谷歌搜索,然后stackoverflowed,直到我脸色发青,我已经尝试了所有似乎对其他人有用的东西。但是当我尝试保存我的新用户对象时,它首先尝试创建一个新的“角色”,而不仅仅是创建一个引用现有角色的新用户对象。
What am I doing wrong?
我究竟做错了什么?
Role myRole = new Role { ID = myUser.Role.ID };
myObjectContext.Roles.Attach(myRole);
myUser.Role = myRole;
if (myUser.ID == 0)
{
myObjectContext.Users.AddObject(myUser);
}
else
{
if (myUser.EntityState == System.Data.EntityState.Detached)
{
myObjectContext.Users.Attach(myUser);
}
myObjectContext.ObjectStateManager.ChangeObjectState(myUser, System.Data.EntityState.Modified);
}
myObjectContext.SaveChanges(SaveOptions.None);
EDIT - AFTER MORE TESTING...
编辑 - 经过更多测试...
Ok.. so I have discovered some portion of the "cause" anyway. I still don't know why it does this and need help.
好吧..所以我已经发现了“原因”的一部分。我仍然不知道为什么它会这样做并需要帮助。
Basically, there are two sets of data I am attaching to my new User object. One is the "Role" which is a FK to a Role table that contains the Role. This shows up as a navigation property on the User like "User.Role".
基本上,我将两组数据附加到我的新用户对象。一个是“角色”,它是包含角色的角色表的 FK。这显示为用户的导航属性,如“User.Role”。
The second set of data is a collection of objects called "FIPS", which are a many-to-many relationship between the User and another table called FIPS. There is a relationship table between them, that simply contains two columns, each a foreign key to User and FIPS, respectively. The FIPS for a user are also a navigation property that is referenced like "User.FIPS".
第二组数据是称为“FIPS”的对象集合,它们是 User 和另一个称为 FIPS 的表之间的多对多关系。它们之间有一个关系表,它只包含两列,每列分别是 User 和 FIPS 的外键。用户的 FIPS 也是像“User.FIPS”一样引用的导航属性。
Here is the whole code showing the assignment of the FIPS and Role to the User object prior to saving the context.
以下是显示在保存上下文之前将 FIPS 和角色分配给用户对象的完整代码。
List<string> fipsList = new List<string>();
foreach (FIPS fips in myUser.FIPS)
{
fipsList.Add(fips.FIPS_Code);
}
myUser.FIPS.Clear();
foreach (string fipsCode in fipsList)
{
FIPS myFIPS = new FIPS { FIPS_Code = fipsCode };
myObjectContext.FIPSCodes.Attach(myFIPS);
myUser.FIPS.Add(myFIPS);
}
Role myRole = new Role { ID = myUser.Role.ID };
myObjectContext.Roles.Attach(myRole);
myUser.Role = myRole;
if (myUser.ID == 0)
{
myObjectContext.Users.AddObject(myUser);
}
else
{
if (myUser.EntityState == System.Data.EntityState.Detached)
{
myObjectContext.Users.Attach(myUser);
}
myObjectContext.ObjectStateManager.ChangeObjectState(myUser, System.Data.EntityState.Modified);
}
myObjectContext.SaveChanges(SaveOptions.None);
I set up my watch to check the status of "myObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added)" to see when things were being added to this.
我设置了我的手表以检查“myObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added)”的状态,以查看何时向其中添加了内容。
As soon as the first Related object is added to the User object, the second Related object that hasn't yet been attached to the context, is added to the context with an EntityState of "Added".
一旦第一个相关对象添加到用户对象,尚未附加到上下文的第二个相关对象就会添加到上下文中,并且 EntityState 为“已添加”。
.... Gonna see if there is a way to avoid attaching the related entities to the User entity until after they have all been attached to the context.
.... 看看是否有办法避免将相关实体附加到 User 实体,直到它们全部附加到上下文之后。
--FOLLOWUP--Ok.. well I changed the order of the code so that the related entities were attached to the context before being assigned to the User entity.. but as soon as the first related entity is assigned, the second related entity is shown as "added" in the ObjectStateEntries. So, then I changed it to the following order:
--FOLLOWUP--好吧..我改变了代码的顺序,以便相关实体在分配给用户实体之前附加到上下文..但是一旦分配了第一个相关实体,第二个相关实体在 ObjectStateEntries 中显示为“已添加”。所以,然后我将其更改为以下顺序:
- Attach all related entities to context.
- Remove existing relationships on the user object to related entity types.
- Assign related entities to user entity.
- Save user entity.
- 将所有相关实体附加到上下文。
- 删除用户对象与相关实体类型的现有关系。
- 将相关实体分配给用户实体。
- 保存用户实体。
And.. now.. it works.. omg it works... ! =)
而且..现在..它起作用了..天啊它起作用了......!=)
采纳答案by Jeff Ogata
It's been a while since I wrote the code below, but I vaguely recall running into the same problem and it was occurring because the role being added was currently being tracked by the context, so attaching the stub has the effect of adding a new role with the same Id.
我写下面的代码已经有一段时间了,但我依稀记得遇到了同样的问题,这是因为正在添加的角色当前正在被上下文跟踪,因此附加存根具有添加新角色的效果同一个 ID。
In the following code, I check the ChangeTrackerfirst and use an existing entry if the role is being tracked.
在下面的代码中,ChangeTracker如果正在跟踪角色,我会检查第一个并使用现有条目。
// add roles that are in dto.Roles, but not in resource.Roles
// use the change tracker entry, or add a stub role
var rolesToAdd = fromDto.Roles.Where(r => !toResource.Roles.Any(role => role.Id == r)).ToList();
var roleEntries = dbContext.ChangeTracker.Entries<Role>();
foreach (var id in rolesToAdd)
{
var role = roleEntries.Where(e => e.Entity.Id == id).Select(e => e.Entity).FirstOrDefault();
if (role == null)
{
role = new Role { Id = id };
dbContext.Set<Role>().Attach(role);
}
toResource.Roles.Add(role);
}
回答by Nuffin
Try using this instead of the first three lines (which shouldn't be necessary at all, if the user object already knows it's role's ID and is discarded anyway):
尝试使用它而不是前三行(这根本不是必需的,如果用户对象已经知道它的角色 ID 并且无论如何都被丢弃了):
int id = myUser.Role.ID; // Role should be NULL, if the user is actually new...
// could it be that you wanted to write myUser.RoleID?
Role myRole = myObjectContext.Roles.FirstOrDefault(x => x.ID == id);
myUser.Role = myRole;
回答by ken2k
Why are you creating a new instance of your Roleentity if it already exists in the database?
Role如果您的实体已经存在于数据库中,为什么要创建它的新实例?
Anyway, if you want to manually attach your new instance to the context, it should work if the ID of the attached instance exists in the database. But in your case the following lines are a bit strange:
无论如何,如果您想手动将新实例附加到上下文,如果附加实例的 ID 存在于数据库中,它应该可以工作。但是在您的情况下,以下几行有点奇怪:
Role myRole = new Role { ID = myUser.Role.ID };
myObjectContext.Roles.Attach(myRole);
myUser.Role = myRole;
You first create a new Role that has an ID that comes from an existing Roleinstance (myUser.Role) then you attach your new instance then finally you affect again your instance to the user it comes from.
There's definitely something wrong here.
If your Role already exists (and it appears to be the case here as you wrote myUser.Role.IDon the first line, so I assume), why are you creating a new instance.
您首先创建一个具有来自现有Role实例 ( myUser.Role)的 ID 的新角色,然后附加您的新实例,最后您再次将您的实例影响到它来自的用户。这里肯定有问题。如果您的角色已经存在(并且您myUser.Role.ID在第一行中所写的情况似乎如此,所以我假设),您为什么要创建一个新实例。
Drop those 3 lines.
Get your Role from the database. Then affect the Rolethat comes from the database to the myUser.Roleproperty.
删除那 3 行。从数据库中获取您的角色。然后影响Role来自数据库的myUser.Role属性。
回答by Sachin
This is how I did it in my case.
在我的案例中,我就是这样做的。
Its a similar case where Itemcontains ICollection<Attribute>.Here no update is done , adding already existing attributeto the itemis needed.
它与Itemcontains 的情况类似。ICollection<Attribute>这里没有更新,需要将现有的添加attribute到item。
First I loopedthrough each attributeinside the item.
首先我looped通过每个attribute里面的item.
I had to first detach it from the local
我必须先将它与本地分离
context.Set<Model.Attribute>().Local
.Where(x => x.Id == attr.Id)
.ToList().ForEach(p => context.Entry(p).State = EntityState.Detached);
Then I attached .
然后我附上了。
context.Set<Model.Attribute>().Attach(attr);
Then I reloaded the datas to it .
然后我将数据重新加载到它。
context.Entry(attr).Reload();

