I'm using Entity Framework code first to manage a database backstore for my users. I have an "add role to user" operation that pulls a user from the db, adds that user to the role, and then saves changes. However, when I do this a new copy of the user is inserted into the database with a new/different ID (unique key) than the user I pulled from the db and I'm not sure why. Any thoughts on why this is happening?
IEnumerable<long>开发者_如何学编程 usersIdsToGiveRole = from u in vm.UsersNotInSelectedRole where u.IsSelected select u.Id; // say, yields "5"
IEnumerable<User> usersToGiveRole = _userRepository.InternalUsers.Where(u => usersIdsToGiveRole.Contains(u.ID)); // gets user with ID 5
foreach (var user in usersToGiveRole)
{
selectedRole.UsersWithRole.Add(user);
}
_roleRepository.SaveChanges(); // creates new user with ID 6 cloning all other fields of user 5
Just a guess: You seem to have separate ObjectContexts for _userRepository
and for _roleRepository
. By loading usersToGiveRole
from the _userRepository
you attach to this context. selectedRole
seems to be attached to the other context of _roleRepository
. When you add the user
to selectedRole.UsersWithRole
you add it to this second context (user
is now in added
state in the context of _roleRepository
). When you call SaveChanges
of this context now a new User object is created in the database.
Solution: Make sure that you only use one single context in both repositories.
Edit
In short what I mean:
Don't do this:
class UserRepository
{
private readonly MyContext _context;
public UserRepository()
{
_context = new MyContext();
}
public void SaveChanges()
{
_context.SaveChanges();
}
}
class RoleRepository
{
private readonly MyContext _context;
public RoleRepository()
{
_context = new MyContext();
}
public void SaveChanges()
{
_context.SaveChanges();
}
}
...
var userRepository = new UserRepository();
var roleRepository = new RoleRepository();
// CRUD
userRepository.SaveChanges();
// perhaps other CRUD
roleRepository.SaveChanges();
Instead do this:
class UserRepository
{
private readonly MyContext _context;
public UserRepository(MyContext context)
{
_context = context;
}
}
class RoleRepository
{
private readonly MyContext _context;
public RoleRepository(MyContext context)
{
_context = context;
}
}
...
using (var context = new MyContext())
{
var userRepository = new UserRepository(context);
var roleRepository = new RoleRepository(context);
// CRUD
context.SaveChanges();
}
The context (or Unit of Work) is always a level above the repositories, should be created outside and injected into the repos.
精彩评论