Инфраструктура сущностей обновляет многие отношения: виртуальные или нет

Я использую EF4 (не первый код) с года, поэтому я не эксперт в этом. Я сомневаюсь в использовании отношений "многие ко многим" в отношении обновления n обновлений.

Я читал где-то в stackoverflow (я больше не могу найти URL-адрес), что одно решение - обновить существующее отношение "многие-ко-многим" - значит не объявлять "виртуальное" свойство; но, если я делаю так, двигатель не может загружать данные с легкой загрузкой.

Можете ли вы объяснить мне причину? Otherwire, не могли бы вы помочь мне найти некоторые интересные документы по этой теме?

ТНХ

Ответы

Ответ 1

Вы можете обновить отношения "многие-ко-многим" таким образом (в качестве примера, который дает пользователю 3 роль 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 объявлена ​​как virtual, строка user.Roles.Add(role); действительно вызовет ленивую загрузку, что означает, что все роли для пользователя загружаются сначала из базы данных, прежде чем добавлять новую роль.

Это действительно беспокоит, потому что вам не нужно загружать всю коллекцию Roles, чтобы добавить новую роль пользователю.

Но это не означает, что вам нужно удалить ключевое слово virtual и вообще отказаться от ленивой загрузки. Вы можете просто отключить ленивую загрузку в этой конкретной ситуации:

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

Если вы хотите обновить всю коллекцию ролей пользователя, я бы предпочел загружать оригинальные роли с нетерпением загрузки (= Include). Вам нужен этот список в любом случае, чтобы удалить некоторые роли, поэтому вам не нужно ждать, пока ленивая загрузка не извлечет их из базы данных:

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

Вместо того, чтобы загружать новые роли из базы данных, вы также можете присоединить их, поскольку в этом примере вы знаете значение свойства ключа. Вы также можете удалить точно отсутствующие роли вместо очистки всей коллекции и вместо повторного добавления существующих ролей:

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