System.Data.Entity.Infrastructure.dbupdateException: An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details. —> System.Data.UpdateException: A relationship from the ‘User_CurrentRole’ AssociationSet is in the ‘Added’ state. Given multiplicity constraints,a corresponding ‘User_CurrentRole_Source’ must also in the ‘Added’ state.
我期望创建一个新角色并与现有用户相关联.
我做错了什么,这有可能首先在EF 4.1代码中实现吗?错误消息似乎表明它需要用户和角色都处于添加状态,但我正在修改一个exising用户,那怎么可能呢?
注意事项:我想避免修改实体的结构(例如,通过引入实体上可见的外键属性),并且在数据库中我希望用户有一个指向Role的外键(不是其他方式).我也不准备转移到自我跟踪实体(除非没有别的办法).
以下是实体:
public class User { public int UserId { get; set; } public string Name { get; set; } public Role CurrentRole { get; set; } } public class Role { public int RoleId { get; set; } public string Description { get; set; } }
这是我正在使用的映射:
public class UserRolesContext : DbContext { public DbSet<User> Users { get; set; } public DbSet<Role> Roles { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<User>().HasKey(u => u.UserId); modelBuilder.Entity<Role>().HasKey(r => r.RoleId); modelBuilder.Entity<User>().Hasrequired(u => u.CurrentRole); } }
我用这个预先填充数据库:
public class UserInitializer : DropCreateDatabaseAlways<UserRolesContext> { protected override void Seed(UserRolesContext context) { context.Users.Add(new User() {Name = "Bob",CurrentRole = new Role() {Description = "Builder"}}); context.SaveChanges(); } }
最后,这是失败的测试:
[TestMethod] public void CanModifyDetachedUserWithRoleAndReattach() { Database.Setinitializer<UserRolesContext>(new UserInitializer()); var context = new UserRolesContext(); // get the existing user var user = context.Users.AsNoTracking().Include(c => c.CurrentRole).First(u => u.UserId == 1); //modify user,and attach to a new role user.Name = "MODIFIED_USERNAME"; user.CurrentRole = new Role() {Description = "NEW_ROLE"}; var newContext = new UserRolesContext(); newContext.Users.Attach(user); // attachment doesn't mark it as modified,so mark it as modified manually newContext.Entry(user).State = EntityState.Modified; newContext.Entry(user.CurrentRole).State = EntityState.Added; newContext.SaveChanges(); var verificationContext = new UserRolesContext(); var afterSaveUser = verificationContext.Users.Include(c => c.CurrentRole).First(u => u.UserId == 1); Assert.AreEqual("MODIFIED_USERNAME",afterSaveUser.Name,"User should have been modified"); Assert.IsTrue(afterSaveUser.CurrentRole != null,"User used to have a role,and should have retained it"); Assert.AreEqual("NEW_ROLE",afterSaveUser.CurrentRole.Description,"User's role's description should have changed."); } } }
当然这是一个被覆盖的场景,我猜这是我在定义模型映射的方式中缺少的东西?
解决方法
Role newRole = user.CurrentRole; // Store the new role to temp variable user.CurrentRole = new Role { Id = oldRoleId }; // Simulate old role from passed Id newContext.Users.Attach(user); newCotnext.Entry(user).State = EntityState.Modified; newContext.Roles.Add(newRole); user.CurrentRole = newRole; // Reestablish the role so that context correctly set the state of the relation with the old role newContext.SaveChanges();
最简单的解决方案是从数据库加载旧状态,并将更改从新状态合并到加载(附加)状态.通过暴露FK属性也可以避免这种情况.
顺便说一句.你的模型不是一对一的,而是一对一的角色可以分配给多个用户 – 如果是一对一的话,它会更复杂,因为你必须在创建一个新角色之前删除旧角色.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。