且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

实体框架更新多对多的关系:虚拟与否

更新时间:2022-11-05 10:13:40

您可以更新一个多一对多的关系这种方式(如这给用户3角色5为例):

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();
}

如果在 User.Roles 收集被声明为虚拟 user.Roles 。新增(角色); 确实会触发延迟加载,这意味着的所有的为用户角色首先从数据库中添加新角色之前加载

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();
}

修改

如果您想更新我会preFER与预先加载加载原始角色的用户的整个角色集合(= 包含)。你需要这个名单反正可能删除一些角色,所以你不需要等到延迟加载从数据库中获取它们:

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();
}