Я пытаюсь перечислить все элементы с дополнительным столбцом, описывающим, принадлежит ли он текущему пользователю.
Поэтому я ищу запрос Linq, который генерирует что-то вроде следующего SQL:
SELECT *,
CASE WHEN
EXISTS (
SELECT NULL FROM OwnedItems
WHERE OwnedItems.UserId = @UserId AND OwnedItems.ItemId = Items.Id
)
THEN 'true'
ELSE 'false'
END AS Owned
FROM Items;
В соответствии с Интернетом и успешным экспериментом LinqPad этот код должен работать.
from item in Items
select new
{
Owned = OwnedItems.Any(own => own.UserId == userId && own.ItemId == item.Id),
Item = item
}
В LinqPad этот код генерирует тот же SQL, что и я.Но в моем проекте это происходит совсем по-другому.
Мой код - это проект .Net Core 2.1, использующий Entity Framework Core 2.1.Поскольку это базовый проект, я не могу напрямую протестировать его в LinqPad, поскольку он еще не поддерживается.
В моем проекте этот код приводит к нефильтрованному оператору SELECT, запрашивающему каждый элемент, а затем для каждого из нихотдельный запрос, чтобы проверить, существует ли он в таблице OwnedItems.Например:
1 экземпляр этого запроса выполняется:
Executed DbCommand (68ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT *
FROM [Items] AS [item]
Далее следуют сотни этих запросов, для выполнения которых требуется несколько секунд:
Executed DbCommand (32ms) [Parameters=[@__userId_0='?' (DbType = Int32), @_outer_Id='?' (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SELECT CASE
WHEN EXISTS (
SELECT 1
FROM [OwnedItems] AS [ownedItems]
WHERE ([ownedItems].[UserId] = @__userId_0) AND ([ownedItems].[ItemId] = @_outer_Id))
THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END
Дополнительная информация,может быть, это поможет: если я использую ту же строку в качестве части предложения where, она отлично работает.
var q = from item in Items
where OwnedItems.Any(o => o.UserId == userId && o.ItemId == item.Id)
select item;
Приведенный выше linq приводит к получению этого замечательного sql:
SELECT *
FROM [Items] AS [item]
WHERE EXISTS (
SELECT 1
FROM [OwnedItems] AS [o]
WHERE ([o].[UserId] = @__userId_0) AND ([o].[ItemId] = [item].[Id]))
Примечания:
Приведенный выше код был искажен вручную, поэтому там могут быть опечатки.Пожалуйста, не обращайте на них внимания.
Я понимаю, что этот конкретный запрос может быть выполнен с использованием левого соединения и проверки на нулевые значения, но мой фактический более сложный, и мне нужны (вложенные) существующие предложения.
ОБНОВЛЕНИЕ ДЛЯ РЕШЕНИЯ
Как указал @KorsG, если Элемент не материализован, генерируется правильный запрос.Я обнаружил, что не материализация Item работает, даже если я напишу следующее:
from item in Items
select new
{
Owned = OwnedItems.Any(own => own.UserId == userId && own.ItemId == item.Id),
// Item = item //THIS LINE GENERATES BAD QUERY
Item = new Item {
Id = item.Id,
Name = item.Name,
...
[Literally every single property listed one by one] = item.CorrespondingProperty
...
}
}
Так что я могу на самом деле материализовать полный элемент, мне просто нужно явно ввести каждое последнее свойство.FUN!