我正在尝试创建具有特定角色的新用户对象。 “角色”是EF中的现有实体。我用谷歌搜索了,并且堆栈溢出,直到我脸色发青,我已经尝试了所有似乎对其他人都有用的东西。但是当我尝试保存我的新用户对象时,它首先尝试创建一个新的“角色”,而不是仅仅创建一个引用现有角色的新用户对象。
我做错了什么?
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);
编辑 - 更多测试后...
好吧..所以我无论如何都发现了“原因”的某些部分。我仍然不知道为什么会这样做并需要帮助。
基本上,我附加到我的新User对象有两组数据。一个是“角色”,它是包含角色的角色表的FK。这在用户上显示为“User.Role”的导航属性。
第二组数据是一组名为“FIPS”的对象,它们是用户与另一个名为FIPS的表之间的多对多关系。它们之间有一个关系表,它只包含两列,每列分别是User和FIPS的外键。用户的FIPS也是一个导航属性,引用类似“User.FIPS”。
以下是整个代码,显示在保存上下文之前将FIPS和Role分配给User对象。
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);
我设置我的手表以检查“myObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added)”的状态,以查看事情何时被添加到此。
只要将第一个Related对象添加到User对象,就会将尚未附加到上下文的第二个Related对象添加到EntityState为“Added”的上下文中。
....看看是否有办法避免将相关实体附加到用户实体,直到它们全部附加到上下文之后。
- 随访 - 好吧..我改变了代码的顺序,以便在分配给用户实体之前将相关实体附加到上下文..但是一旦分配了第一个相关实体,第二个相关实体就显示为“已添加” “在ObjectStateEntries中。 所以,然后我把它改成了以下顺序:
而且......现在......它的确有效......它的作用...... =)
答案 0 :(得分:4)
自从我编写下面的代码以来已经有一段时间了,但我依旧回忆起遇到同样的问题并且它正在发生,因为正在添加的角色当前正被上下文跟踪,因此附加存根具有添加具有相同Id的新角色。
在以下代码中,我首先检查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);
}
答案 1 :(得分:0)
为什么要创建Role
实体的新实例(如果它已存在于数据库中)?
无论如何,如果要将新实例手动附加到上下文,如果数据库中存在附加实例的ID,则它应该有效。但在你的情况下,以下几行有点奇怪:
Role myRole = new Role { ID = myUser.Role.ID };
myObjectContext.Roles.Attach(myRole);
myUser.Role = myRole;
您首先创建一个具有来自现有Role
实例(myUser.Role
)的ID的新角色,然后附加新实例,最后再次影响您的实例。
这里肯定有问题。
如果您的角色已经存在(并且在第一行编写myUser.Role.ID
时似乎就是这种情况,那么我假设),为什么要创建新实例。
删除这3行。
从数据库中获取您的角色。然后影响来自数据库的Role
到myUser.Role
属性。
答案 2 :(得分:0)
我就是这样做的。
类似的情况Item
包含ICollection<Attribute>
。如果没有更新,则需要将已存在的attribute
添加到item
。
首先looped
通过attribute
内的每个item
。
我必须先把它从当地分开
context.Set<Model.Attribute>().Local
.Where(x => x.Id == attr.Id)
.ToList().ForEach(p => context.Entry(p).State = EntityState.Detached);
然后我附上了。
context.Set<Model.Attribute>().Attach(attr);
然后我将数据重新加载到它。
context.Entry(attr).Reload();
答案 3 :(得分:-1)
尝试使用它而不是前三行(如果用户对象已经知道它的角色的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;