В моей модели данных есть много таблиц «объединения», реализующих отношения «многие ко многим», и они имеют составные первичные ключи, по одному для каждой стороны отношения. Например, у меня есть объект room и объект document, а таблица room-document (очевидно) имеет room_id и document_id. У меня также есть сущность product и аналогичная таблица product-document с Product_id и document_id и так далее.
В EF Core 8 (и Npgsql.EntityFrameworkCore.PostgreSQL) я создаю универсальную службу, которая будет работать со всеми типами одного типа, например со всеми типами xxx-документов. Начиная с нестандартной версии, привязанной к комнатам:
Код: Выделить всё
var result = await
someBaseQuery // has int MainEntity and int DocumentId
.LeftJoin // LeftJoin is just an extension on IQueryable with GroupJoin + SelectMany
(
context.Set(),
left => new { left.MainEntityId, left.DocumentId }, // outer-key selector
right => new { MainEntityId = right.RoomId, right.DocumentId } // inner-key selector
)
.ToListAsync();
При создании универсального сервиса в селекторе внутреннего ключа right.xxxId станет неоднородным, поэтому мне нужно динамически создавать лямбда-выражение (это нормально). Но чтобы код оставался читаемым, я хотел построить выражение в другом методе, поэтому анонимные типы не будут работать, поскольку лямбда-выражение должно быть возвращаемым значением метода.
Один очевидный выбор создает составной тип:
Код: Выделить всё
record Composite
{
public int MainEntityId { get; init; }
public int DocumentId { get; init; }
}
(примечание: для записи необходимо использовать инициализацию члена, при использовании конструктора по умолчанию EF не может отслеживать свойства дальше. Это может быть хорошо для окончательного результата, но если нужно работать дальше, нужно использовать это или синтаксис класса+свойства/поля)
Итак, результат будет следующим:
Код: Выделить всё
.LeftJoin
(
context.Set(),
left => new Composite { MainEntityId = left.MainEntityId, DocumentId = left.DocumentId },
GetKeySelectorExpression() // returns Composite
)
Это кажется хорошим, но, к сожалению, приведет к исключению InvalidOperationException: «Выражение LINQ... не удалось перевести». Причина в том, что EF необходимо преобразовать выражение в SQL и не может работать с моим типом Composite (ссылка:
https://github.com/dotnet/efcore/issues ... -858807805)
Я также прочитал:
https://github.com/dotnet/efcore/issues/10784 и
https://github.com/dotnet/efcore/issues/23775 и попробовал использовать ConfigurationBuilder. Properties().HaveConversion, но пока безуспешно. Есть предложения?
Подробнее здесь:
https://stackoverflow.com/questions/787 ... y-selector