2

Я хочу сравнить записи, чтобы увидеть, есть ли различия между ними.IEquatable прерывает загрузку объектов Entity Framework

Person стол:

ID Name   Address 
-------------------------------- 
1  John Smith 123 A Street 
2  John Smith 123 A Street 
3  John Smith 234 B Street 

Записи 1 и 2 "равны". Записи 2 и 3 «не равны».

Я внедрил IEquatable на модель Person следующим образом.

public static bool operator ==(Person p1, Person p2) 
{ 
    if (System.Object.ReferenceEquals(p1, p2)) return true; 

    return p1.Equals(p2); 
} 

public static bool operator !=(Person p1, Person p2) 
{ 
    return !(p1== p2); 
} 

public bool Equals(Person other) 
{ 
    if (System.Object.ReferenceEquals(this, other)) return true; 

    if (Name != other.Name) return false; 
    if (Address != other.Address) return false; 

    return true; 
} 

public override bool Equals(object obj) 
{ 
    Person person = obj as Person; 
    if (person == null) return false; 

    return Equals(person); 
} 

public override int GetHashCode() 
{ 
    unchecked 
    { 
     int hash = (int)2166136261; 
     hash = hash * 25165843^(Name != null ? Name .GetHashCode() : 0); 
     hash = hash * 25165843^(Address != null ? Address.GetHashCode() : 0); 

     return hash; 
    } 
} 

Вопрос заключается в том, что когда Persons ICollection от навигационного имущества материализуется. В нем отсутствуют записи, «равные» друг другу (т. Е. Возвращается одна запись John Smith 123 A Street). Я предполагаю, что это связано с тем, что по умолчанию он рассматривает разные объекты, имеющие уникальные первичные ключи. Переопределяя равные значения, он считает, что обе записи являются одной и той же сущностью.

Скриншот показывает Addresses вместо Persons: (верх с IEquatable, дно без) enter image description here

//Addresses Definition (generated code) 
public virtual ICollection<Address> Addresses { get; set; } 

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

+0

Можете ли вы показать объявление свойства 'Addresses' на' EmployeeElection'? –

+0

@CharlesMager Конечно, я добавил его под скриншотом. Это часть генерируемого кода EF. – Shoe

+0

Установили ли вы это как новый Hashset

() 'в конструкторе?Это часто случается в образцах EF. Кроме того, ключевое слово 'virtual' допускает ленивую загрузку, созданный прокси EF может использовать' ISet
'(я не знаю специфики!). Множество семантики означает, что дубликаты не могут быть добавлены, и по умолчанию они будут использовать сопоставитель по умолчанию по умолчанию (так что ваш 'Equals' переопределить). Поэтому, хотя запрос может возвращать дубликаты, модель/EF их не покажет. –

ответ

2

Ключ кажется быть в EF source code

в примечаниях к EntityUtil.DetermineCollectionType(Type requestedType) есть эти «rules`:

// The rules are: 
    // If the collection is defined as a concrete type with a publicly accessible parameterless constructor, then create an instance of that type 
    // Else, if HashSet{T} can be assigned to the type, then use HashSet{T} 
    // Else, if List{T} can be assigned to the type, then use List{T} 
    // Else, throw a nice exception. 

Таким образом, из этого было бы увидеть m, что EF будет обновлять HashSet<Address> для вашего свойства навигации. Это будет использовать сопоставитель равенства по умолчанию и предотвратить добавление дубликатов. Поскольку ваша реализация Equals идентифицирует два ваших результата как равные, будет включен только один.

Объекты обычно уникально идентифицированы - переопределение Equals, игнорируя уникальный идентификатор, возможно, неверно. Лучшим решением было бы удалить переопределение и реализовать отдельный IEqualityComparer. Большинство методов, которые используют семантику равенства, возьмут это как аргумент.

+0

Это определенно это. Спасибо. – Shoe

0

Не используйте IEquatable и т. Д., Создайте свой собственный AreEquivalent, или IsEquivalentTo метод.

+1

Как бы такие методы помогли ему получить отдельные элементы из базы данных? – Servy

+0

Используйте '.GroupBy (x => new {x.Name, x.Address}). Выберите (g => g.First())' ..., который эффективно 'DistinctBy' –

2

IEquatable и IEqualityComparer - это концепции, которые в значительной степени исключительны для LINQ для объектов. EF не имеет практического способа перевода любого определения «равенства», определенного таким образом в SQL, и поэтому не может выполнять такие операции.

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

var query = context.Table.GroupBy(row => new 
    { 
     row.Name, 
     row.Address, 
    }) 
    .Select(group => group.FirstOrDefault()); 
+0

У меня нет проблемы получая отдельные элементы, я заинтересован в ответе на вопрос «У меня есть два объекта Person, они логически одинаковы?». – Shoe

+0

@Shoe Независимо от того, если ваш запрос не возвращает то, что вы хотите, нужно посмотреть на этот запрос и настроить, какие операторы он использует или как он их использует. Первый абзац ответа по-прежнему стоит; 'IEquatable' не может быть интерпретирован вообще EF. – Servy

+0

Я понимаю это, но у меня нет никаких вопросов. Если я просто использую код 'context.Persons.ToList()', то материализованный набор не будет содержать более одной записи, равной «равным». Мне нужно ответить на вопрос в моем комментарии выше, но реализация IEquatable, похоже, полностью разрушает загрузку объектов. – Shoe

Смежные вопросы