Запретить DbSet.AddRange () изменять локальную переменную с помощью. Net Core Entity Framework - PullRequest
1 голос
/ 20 июня 2020

Мне нужно некоторое руководство по пониманию того, что происходит «за капотом», когда метод addRange() вызывается в DbSet.

Я обнаружил метод расширения DbContext, который (должен) легко обновлять элементы в отношении "многие-ко-многим". Ссылка здесь: { ссылка }.

Вот расширение на случай, если вопрос удален:

public static void TryUpdateManyToMany<T, TKey>(this DbContext db, IEnumerable<T> currentItems, IEnumerable<T> newItems, Func<T, TKey> getKey) where T : class
{
    db.Set<T>().RemoveRange(currentItems.Except(newItems, getKey));
    db.Set<T>().AddRange(newItems.Except(currentItems, getKey));
}

public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other, Func<T, TKey> getKeyFunc)
{
    return items
        .GroupJoin(other, getKeyFunc, getKeyFunc, (item, tempItems) => new { item, tempItems })
        .SelectMany(t => t.tempItems.DefaultIfEmpty(), (t, temp) => new { t, temp })
        .Where(t => ReferenceEquals(null, t.temp) || t.temp.Equals(default(T)))
        .Select(t => t.t.item);
}

Насколько я понимаю, этот метод расширения сравнивает две коллекции items и отмечает те, которые были удалены, как Removed и отмечает те, которые были добавлены как Added в ChangeTracker. Однако при пошаговом выполнении кода я заметил, что он фактически изменял мою локальную переменную file.FileApprovers с новыми добавленными значениями.

введите описание изображения здесь

Обратите внимание, что к нижней части этой коллекции добавлены 3 элемента с помощью Type: RadFX.Domain.Models.FileApprover? Они были добавлены в коллекцию после запуска TryUpdateManyToMany. Это вызывает проблему, потому что теперь индексы 0/2 и 1/3 дублируются. Индекс 4 - это новое значение, которое было добавлено при отправке формы, и мне все еще нужно использовать file.fileApprovers под этим методом, чтобы увидеть «существующих» утверждающих. Если AddRange() действительно добавляет новые элементы в коллекцию, почему тогда RemoveRange() не удаляет первые два значения?

Дополнительный контекст в моем коде:

// update m-2-m table when adding or remove users
db.TryUpdateManyToMany(file.FileApprovers, approvers
    .Select(x => new FileApprover
    {
        FileId = file.Id,
        Username = x.Username
    }), x => x);

// I figured that file.FileApprovers would be left unchanged so that I can
// get the usernames of the "existing approvers"
// for example, if I had 2 approvers, but added another one on form submission
// this variable should still just contain 2 approvers
// aka the items of Type: Castles.Proxies.FileApproverProxy

var existingApprovers = file.FileApprovers.Select(fa => fa.Username).ToList();

FileApprover.cs

// this is a join table class between files and approvers
public class FileApprover
{
    [Key]
    public string Username { get; set; }
    [Key]
    public int FileId { get; set; }
    public virtual RtdbFile File { get; set; }
    public virtual User User { get; set; }
}

Переменная approvers, используемая во втором аргументе TryUpdateManyToMany(), - это List из User элементов, которые были отправлены из формы:

User.cs

public class User
{
    [Key]
    public string Username { get; set; }
    public int BadgeNumber { get; set; }
    public string DisplayName { get; set; }

    public virtual ICollection<FileApprover> FileApprovers { get; set; }
}

1 Ответ

0 голосов
/ 21 июня 2020

Я думаю, ваша проблема не в том, как работает DbSet.AddRange, а скорее в том, как вы вызываете TryUpdateManyToMany:

// update m-2-m table when adding or remove users
db.TryUpdateManyToMany(file.FileApprovers, approvers
    .Select(x => new FileApprover
    {
        FileId = file.Id,
        Username = x.Username
    }), x => x);

x => x, являющееся проблемной частью c. Вы пытаетесь выполнить соединение на основе ссылочного равенства x, а не заданного свойства.

Что-то вроде этого должно работать нормально:

// update m-2-m table when adding or remove users
db.TryUpdateManyToMany(file.FileApprovers, approvers
    .Select(x => new FileApprover
    {
        FileId = file.Id,
        Username = x.Username
    }), x => x.FileId);
...