Я пытаюсь использовать код EF 4.1 First, чтобы смоделировать простые отношения пользователя, имеющего одну роль. Когда я пытаюсь сохранить существующего пользователя с новой ролью в другом контексте (использующем другой контекст для имитации обхода клиент-сервер), я получаю следующее исключение:
System.Data.Entity.Infrastructure.DbUpdateException: произошла ошибка при сохранении сущностей, которые не предоставляют свойства внешнего ключа для своих отношений. Свойство EntityEntries вернет значение NULL, поскольку один объект не может быть определен как источник исключения. Обработка исключений при сохранении может быть упрощена путем предоставления свойств внешнего ключа в типах объектов. Смотрите InnerException для подробностей. ---> System.Data.UpdateException: отношение из AssociationSet 'User_CurrentRole' находится в состоянии 'Добавлено'. Учитывая ограничения множественности, соответствующий «User_CurrentRole_Source» также должен находиться в состоянии «Добавлено».
Я ожидаю, что новая роль будет создана и связана с существующим пользователем.
Что я делаю не так, возможно ли в первую очередь добиться этого в коде EF 4.1? Похоже, что сообщение об ошибке указывает на то, что ему нужно, чтобы и пользователь, и роль были в добавленном состоянии, но я изменяю существующего пользователя, так как это может быть?
Вещи, на которые следует обратить внимание: я бы хотел избежать изменения структуры сущностей (например, путем введения свойств внешнего ключа, видимых на сущностях), и в базе данных я бы хотел, чтобы у пользователя был внешний ключ, указывающий на роль (А не наоборот). Я также не готов перейти к Самопроверка (если нет другого пути).
Вот сущности:
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.");
}
}
}
Конечно, это сценарий, который я рассмотрел, я думаю, что я чего-то упускаю в том, как я определил отображение модели?