modelBuilder.Entity
При выполнении стандартного запроса LINQ любые заказы, содержащие неуправляемый автомобиль, будут исключаться из результата. Ниже показан оператор LINQ и генерированный оператор SQL:
// Код C#
var orders = Context.Orders.ToList();
/* Сгенерированный запрос SQL */
SELECT [o].[Id], [o].[CarId], [o].[CustomerId], [o].[TimeStamp]
FROM [Dbo].[Orders] AS [o]
INNER JOIN (SELECT [i].[Id], [i].[IsDrivable]
FROM [Dbo].[Inventory] AS [i]
WHERE [i].[IsDrivable] = CAST(1 AS bit)) AS [t]
ON [o].[CarId] = [t].[Id]
WHERE [t].[IsDrivable] = CAST(1 AS bit)
Для удаления фильтра запросов используйте вызов IgnoreQueryFilters(). Вот как выглядит модифицированный оператор LINQ и сгенерированный запрос SQL:
// Код C#
var orders = Context.Orders.IgnoreQueryFilters().ToList();
/* Сгенерированный запрос SQL */
SELECT [o].[Id], [o].[CarId], [o].[CustomerId], [o].[TimeStamp]
FROM [Dbo].[Orders] AS [o]
Здесь уместно предостеречь: исполняющая среда EF Core не обнаруживает циклические глобальные фильтры запросов, поэтому при добавлении фильтров запросов к навигационным свойствам соблюдайте осторожность.
Явная загрузка с глобальными фильтрами запросов
Глобальные фильтры запросов действуют и при явной загрузке связанных данных. Например, если вы хотите загрузить записи Car для Make, то фильтр IsDrivable предотвратит загрузку в память записей, представляющих неуправляемые автомобили. В качестве примера взгляните на следующий фрагмент кода:
var make = Context.Makes.First(x => x.Id == makeId);
Context.Entry(make).Collection(c=>c.Cars).Load();
К настоящему моменту не должен вызывать удивление тот факт, что сгенерированный оператор SQL включает фильтр для неуправляемых автомобилей:
SELECT [i].[Id], [i].[Color], [i].[IsDrivable],
[i].[MakeId], [i].[PetName], [i].[TimeStamp]
FROM [Dbo].[Inventory] AS [i]
WHERE ([i].[IsDrivable] = CAST(1 AS bit)) AND ([i].[MakeId] = 1
С игнорированием фильтров запросов при явной загрузке данных связана небольшая загвоздка. Возвращаемым типом метода Collection() является CollectionEntry, который явно не реализует интерфейс IQueryable. Чтобы вызвать IgnoreQueryFilters(), сначала потребуется вызвать метод Query(), который возвращает экземпляр реализации IQueryable:
var make = Context.Makes.First(x => x.Id == makeId);
Context.Entry(make).Collection(c=>c.Cars).Query().IgnoreQueryFilters().Load();
Тот же процесс применяется в случае использования метода Reference() для извлечения данных из навигационного свойства типа коллекции.
Выполнение низкоуровневых запросов SQL с помощью LINQ
Иногда получить корректный оператор LINQ для компилируемого запроса сложнее, чем просто написать код SQL напрямую. К счастью, инфраструктура EF Core располагает механизмом, позволяющим выполнять низкоуровневые операторы SQL в DbSet. Методы FromSqlRaw() и FromSqlRawInterpolated() принимают строку, которая становится основой запроса LINQ. Такой запрос выполняется на стороне сервера.
Если низкоуровневый оператор SQL не является завершающим (скажем, хранимой процедурой, пользовательской функцией или оператором, который использует общее табличное выражение или заканчивается точкой с запятой), тогда в запрос можно добавить дополнительные операторы LINQ. Дополнительные операторы LINQ наподобие конструкций Include(), OrderBy() или Where() будут объединены с первоначальным низкоуровневым обращением SQL и любыми глобальными фильтрами запросов, после чего весь запрос выполнится на стороне сервера.