У меня странная ситуация с EntityFramework 6 с. NET 4.5 (C#).
У меня (почти) один и тот же запрос в двух разных местах. Но один раз он снова запрашивает базу данных, а второй - запросы к объектам в памяти. И так как я фильтрую по подстроке, это принципиальное отличие:
Структура базы данных - это таблицы Role, Right и кросс-таблица Role_Right
В первый раз я хочу найти все доступные права, которые еще не назначены роли, плюс (и это усложняется) ручной фильтр для сокращения списка результатов:
Role role = ...;
string filter = ...;
var roleRightNames = role.Right.Select(roleRight => roleRight.RightName);
var filteredRights = context.Right.Where(right => !roleRightNames.Contains(right.RightName));
if (!string.IsNullOrWhiteSpace(filter))
{
filteredRights = filteredRights.Where(e => e.RightName.Contains(filter));
}
var result = filteredRights.ToList();
Я не могу использовать IndexOf(filter, StringComparison.InvariantCultureIgnoreCase) >= 0)
, поскольку его нельзя перевести на SQL. Но я в порядке с Contains
, потому что он дает желаемый результат (см. Ниже).
При включении вывода SQL я получаю:
SELECT [Extent1].[RightName] AS [RightName]
FROM [dbo].[Right] AS [Extent1]
WHERE ( NOT ([Extent1].[RightName] IN ('Right_A1', 'Right_A2', 'Right_B1'))) AND ([Extent1].[RightName] LIKE @p__linq__0 ESCAPE '~'
-- p__linq__0: '%~_a%' (Type = AnsiString, Size = 8000)
Это именно то, что я хочу, нечувствительный к регистру поиск по фильтру "_a", чтобы найти, например, 'Right_A3'
Второй раз, когда я хочу отфильтровать существующие связанные права для того же фильтра:
Role role = ...;
string filter = ...;
var filteredRights = string.IsNullOrWhiteSpace(filter)
? role.Right
: role.Right.Where(e => e.RightName.IndexOf(filter, StringComparison.InvariantCultureIgnoreCase) >= 0);
var result = filteredRights.ToList();
На этот раз это заставляет меня использовать IndexOf
, потому что он использует Contains
метод string
вместо преобразования его в SQL LIKE
, а string.Contains
чувствителен к регистру.
Моя проблема в том, что я не могу - исходя из кода - предсказать, когда запрос выполняется к базе данных и когда он выполняется в памяти, и поскольку я не могу использовать IndexOf
в первом запросе и Contains
во втором это кажется немного непредсказуемым для меня. Что происходит, когда однажды второй запрос выполняется первым, а данные еще не находятся в памяти?
Изменить 10 февраля 2020
ОК, поэтому я выяснил, что Основное отличие заключается в. context.Right
имеет тип DbSet
, который является IQueryable
, как и последующий метод расширения Where
. Однако userRole.Right
возвращает ICollection
, который является IEnumerable
, как и последующие Where
. Есть ли способ сделать свойство отношения объекта сущности к IQueryable
? AsQueryable
не работает. Это означает, что все связанные Right
сущности всегда извлекаются из базы данных перед выполнением в памяти Where
. Мы не говорим об огромных объемах данных, и, по крайней мере, сейчас это поведение предсказуемо, но, тем не менее, я считаю его неудачным.