Entity Framework использует свои собственные интеллектуальные методы для обнаружения равенства объектов.Это, например, используется, если вы вызываете SaveChanges
: значения выбранных объектов сопоставляются со значениями обновленных объектов, чтобы определить, требуется ли обновление SQL.
Я не уверен, соответствуют ли ваши определенияравенство нарушит эту проверку на равенство, в результате чего некоторые неизмененные элементы будут обновлены в базе данных, или, что еще хуже, некоторые измененные данные не будут обновлены в базе данных.
Равенство базы данных
Сохранитьпомните, что ваши классы сущностей (классы, которые вы указали в DbSet<...>
) представляют таблицы в вашей базе данных и отношения между таблицами.
Когда два элемента, извлеченные из вашей базы данных, должны представлять один и тот же объект?Это когда они имеют одинаковые значения?Разве у нас не может быть двух людей по имени "Джон Доу", родившихся 4 июля в одной базе данных?
Единственный способ определить, что два извлеченных Persons
из базы данных представляют один и тот же Person
, - это проверить Id.Тот факт, что некоторые значения неосновного ключа отличаются, говорит только о том, что измененные данные не обновляются в базе данных, а не о том, что это Person
.
Переопределить Equals vs Create EqualityComparer
Мой совет - сохранить как можно более простые представления таблиц: только столбцы таблицы (не виртуальные свойства) и отношения между таблицами.(виртуальные свойства).Нет членов, нет методов, ничего.
Если вам нужна дополнительная функциональность, создайте функции расширения классов.Если вам нужны нестандартные методы сравнения на равенство, создайте отдельный инструмент для сравнения на равенство.Пользователи вашего класса могут решить, хотят ли они использовать метод сравнения по умолчанию или ваш специальный метод сравнения.
Все это сопоставимо с различными типами String Comparers: StringComparer.OrdinalIgnorCase
, StringComparer.InvariantCulture
и т. Д.
Вернуться к вашему вопросу
Мне кажется, что вы хотите, чтобы Gpu-компаратор не проверял значение Id: два элемента с разными Id, но одинаковыми значениямидля других свойств считаются равными.
class GpuComparer : EqualityComparer<Gpu>
{
public static IEqualityComparer<Gpu> IgnoreIdComparer {get;} = new GpuComparer()
public override bool Equals(Gpu x, Gpu y)
{
if (x == null) return y == null; // true if both null, false if x null but y not
if (y == null) return false; // because x not null
if (Object.ReferenceEquals(x, y)) return true;
if (x.GetType() != y.GetType()) return false;
// if here, we know x and y both not null, and of same type.
// compare all properties for equality
return x.Cores == y.Cores;
}
public override int GetHasCode(Gpu x)
{
if (x == null) throw new ArgumentNullException(nameof(x));
// note: I want a different Hash for x.Cores == null than x.Cores == 0!
return (x.Cores.HasValue) ? return x.Cores.Value.GetHashCode() : -78546;
// -78546 is just a value I expect that is not used often as Cores;
}
}
Обратите внимание, что я добавил тест для того же типа, потому что если y является производным классом Gpu, и вы бы проигнорировали, что они не того же типа, то, возможно,Равно (x, y), но не равно (y, x), что является одной из предпосылок функций равенства
Использование:
IEqualityComparer<Gpu> gpuIgnoreIdComparer = GpuComparer.IgnoreIdComparer;
Gpu x = new Gpu {Id = 0, Cores = null}
Gpu y = new Gpu {Id = 1, Cores = null}
bool sameExceptForId = gpuIgnoreIdComparer.Equals(x, y);
x и y будут считаться равными
HashSet<Gpu> hashSetIgnoringIds = new HashSet<Gpu>(GpuComparer.IgnoreIdComparer);
hashSetIgnoringIds.Add(x);
bool containsY = hashSetIgnoringIds.Contains(y); // expect true
Сравнение для компьютера будет аналогичным.Помимо того, что вы забыли проверить нулевые значения и типы, я вижу и другие проблемы в том, как вы хотите выполнить проверку на равенство:
- можно присвоить нулевое значение вашей коллекции Gpus.Вы должны решить это, чтобы оно не бросало исключение.Компьютер с нулевым Gpus равен компьютеру с нулевым Gpus?
- Видимо, порядок Gpus для вас не важен: [1, 3] равен [3, 1]
- Видимо, количество раз, когда появляется определенный графический процессор, не имеет значения: [1, 1, 3] равно [1, 3, 3]?
.
class IgnoreIdComputerComparer : EqualityComparer<Computer>
{
public static IEqualityComparer NoIdComparer {get} = new IgnoreIdComputerCompare();
public override bool (Computer x, Computer y)
{
if (x == null) return y == null;not null
if (y == null) return false;
if (Object.ReferenceEquals(x, y)) return true;
if (x.GetType() != y.GetType()) return false;
// equal if both GPU collections null or empty,
// or any element in X.Gpu is also in Y.Gpu ignoring duplicates
// using the Gpu IgnoreIdComparer
if (x.Gpus == null || x.Gpus.Count == 0)
return y.Gpus == null || y.Gpus.Count == 0;
// equal if same elements, ignoring duplicates:
HashSet<Gpu> xGpus = new HashSet<Gpu>(x, GpuComparer.IgnoreIdComparer);
return xGpush.EqualSet(y);
}
public override int GetHashCode(Computer x)
{
if (x == null) throw new ArgumentNullException(nameof(x));
if (x.Gpus == null || x.Gpus.Count == 0) return -784120;
HashSet<Gpu> xGpus = new HashSet<Gpu>(x, GpuComparer.IgnoreIdComparer);
return xGpus.Sum(gpu => gpu);
}
}
TODO: если вы будете использовать большие коллекции Gpus, рассмотрите более умный GetHashCode