You can update a many-to-many relationship this way (as an example which gives user 3 the role 5):
using (var context = new MyObjectContext())
{
var user = context.Users.Single(u => u.UserId == 3);
var role = context.Roles.Single(r => r.RoleId == 5);
user.Roles.Add(role);
context.SaveChanges();
}
If the User.Roles
collection is declared as virtual
the line user.Roles.Add(role);
will indeed trigger lazy loading which means that all roles for the user are loaded first from the database before you add the new role.
This is in fact disturbing because you don't need to load the whole Roles
collection to add a new role to the user.
But this doesn't mean that you have to remove the virtual
keyword and abandon lazy loading altogether. You can just turn off lazy loading in this specific situation:
using (var context = new MyObjectContext())
{
context.ContextOptions.LazyLoadingEnabled = false;
var user = context.Users.Single(u => u.UserId == 3);
var role = context.Roles.Single(r => r.RoleId == 5);
user.Roles = new List<Role>(); // necessary, if you are using POCOs
user.Roles.Add(role);
context.SaveChanges();
}
Edit
If you want to update the whole roles collection of a user I would prefer to load the original roles with eager loading ( = Include
). You need this list anyway to possibly remove some roles, so you don't need to wait until lazy loading fetches them from the database:
var newRolsIds = new List<int> { 1, 2, 5 };
using (var context = new MyObjectContext())
{
var user = context.Users.Include("Roles")
.Single(u => u.UserId == 3);
// loads user with roles, for example role 3 and 5
var newRoles = context.Roles
.Where(r => newRolsIds.Contains(r.RoleId))
.ToList();
user.Roles.Clear();
foreach (var newRole in newRoles)
user.Roles.Add(newRole);
context.SaveChanges();
}
Instead of loading the new roles from the database you can also attach them since you know in the example the key property value. You can also remove exactly the missing roles instead of clearing the whole collection and instead of re-adding the exisiting roles:
var newRolsIds = new List<int> { 1, 2, 5 };
using (var context = new MyObjectContext())
{
var user = context.Users.Include("Roles")
.Single(u => u.UserId == 3);
// loads user with roles, for example role 3 and 5
foreach (var role in user.Roles.ToList())
{
// Remove the roles which are not in the list of new roles
if (!newRoleIds.Contains(role.RoleId))
user.Roles.Remove(role);
// Removes role 3 in the example
}
foreach (var newRoleId in newRoleIds)
{
// Add the roles which are not in the list of user's roles
if (!user.Roles.Any(r => r.RoleId == newRoleId))
{
var newRole = new Role { RoleId = newRoleId };
context.Roles.Attach(newRole);
user.Roles.Add(newRole);
}
// Adds roles 1 and 2 in the example
}
// The roles which the user was already in (role 5 in the example)
// have neither been removed nor added.
context.SaveChanges();
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…