Мне нужно некоторое руководство по пониманию того, что происходит «за капотом», когда метод 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; }
}