Сообщение об исключении, конечно, смешно и не имеет ничего общего с реальной проблемой, которая является выражением
a => a.Address.Equals(address)
Оно является частью дерева выражений IQueryable
, поэтому EF Core пытается преобразовать его в SQL.
Объектно-ориентированные функции, такие как инкапсуляция, плохо сочетаются с преобразованием выражений, которое основано на видимости и знаниях. EF Core не является декомпилятором, он не видит реализацию вашего метода Equals
. Что обычно они делают с неизвестными методами, так это генерируют исключение времени выполнения, предлагающее вам либо использовать переводимую конструкцию, либо явно переключиться на оценку клиента.
Однако типы сущностей имеют особую обработку. EF Core пытается поддерживать преобразование сравнения на равенство (==
, '! = ,
Equals`), неявно преобразовывая его в сравнение PK (первичный ключ).
И здесь возникает проблема с вашим собственным объектом тип. Обратите внимание, что принадлежащие типы сущностей по-прежнему являются типами entity , но ссылочные принадлежащие типы, такие как ваш Address
, не имеют собственного PK, отсюда и исключение.
Конечно, то, что они делают, является ошибкой, но даже если они "исправят" это, исправление будет просто другим исключением времени выполнения.
Решение, конечно, состоит в том, чтобы не использовать метод Equals
, а явное сравнение членов, например
a => a.Address.StreetNumber.ToUpper() == address.StreetNumber.ToUpper()
&& a.Address.StreetName.ToUpper() == address.StreetName.ToUpper()
&& a.Address.City.ToUpper() == address.City.ToUpper()
&& a.Address.State.ToUpper() == address.State.ToUpper()
&& a.Address.ZipCode.ToUpper() == address.ZipCode.ToUpper()
Обратите внимание, что сравнение строк контролируется базой данных, поэтому явное ToUpper()
необходимо, если вам нужно принудительно использовать сравнение без учета регистра.
Теперь я знаю, что это дублирование кода и нарушение инкапсуляции, но это единственный способ (кроме случаев, когда вы используете какую-то стороннюю библиотеку, например Лямбда-инъекция от NeinLinq.EntityFrameworkCore , DelegateDecompiler et c.), Чтобы получить фильтрацию на стороне сервера.
Потому что фильтрация на стороне клиента после чтения всей таблицы, например
_context.Apartments.AsEnumerable().FirstOrDefault(a => a.Address.Equals(address))
, будет убийца производительности (и является причиной удаления неявной оценки клиента в EF Core 3.0+), не считая отсутствия естественной поддержки async
(требуется дополнительный пакет, который, в свою очередь, вызывает проблемы с EF Core DbSet
s - см. Преобразование запросов EF Core из 2.2 в 3.0 - asyn c await ).