Придерживаясь в книге темы автомобилей, предположим, что все записи
Car
, которые не являются управляемыми, должны отфильтровываться из нормальных запросов. Вот как можно добавить глобальный фильтр запросов с использованием Fluent API:
modelBuilder.Entity<Car>(entity =>
{
<b> entity.HasQueryFilter(c => c.IsDrivable == true);</b>
entity.Property(p => p.IsDrivable).HasField("_isDrivable").
HasDefaultValue(true);
});
Благодаря такому глобальному фильтру запросов запросы, вовлекающие сущность
Car
, будут автоматически отфильтровывать неуправляемые автомобили. Скажем, запуск следующего запроса LINQ:
var cars = Context.Cars.ToList();
приводит к выполнению показанного ниже оператора 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)
Если вам нужно просмотреть отфильтрованные записи, тогда добавьте в запрос LINQ вызов
IgnoreQueryFilters()
, который отключает глобальные фильтры запросов для каждой сущности в запросе LINQ. Запуск представленного далее запроса LINQ:
var cars = Context.Cars.IgnoreQueryFilters().ToList();
инициирует выполнение следующего оператора SQL:
SELECT [i].[Id], [i].[Color], [i].[IsDrivable], [i].[MakeId],
[i].[PetName], [i].[TimeStamp]
FROM [Dbo].[Inventory] AS [i]
Важно отметить, что вызов
IgnoreQueryFilters()
удаляет фильтр запросов для всех сущностей в запросе LINQ, в том числе и тех, которые задействованы в вызовах
Include()
или
Thenlnclude()
.
Глобальные фильтры запросов на навигационных свойствах
Глобальные фильтры запросов можно также устанавливать на навигационных свойствах. Пусть вам необходимо отфильтровать любые заказы, которые содержат экземпляр
Car
, представляющий неуправляемый автомобиль. Фильтр создается на навигационном свойстве
CarNavigation
сущности
Order
:
modelBuilder.Entity<Order>().HasQueryFilter(e => e.CarNavigation.IsDrivable);
При выполнении стандартного запроса 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<Make,Car>
, который явно не реализует интерфейс
IQueryable<T>
. Чтобы вызвать
IgnoreQueryFilters()
, сначала потребуется вызвать метод
Query()
, который возвращает экземпляр реализации
IQueryable<Car>
:
var make = Context.Makes.First(x => x.Id == makeId);
Context.Entry(make).Collection(c=>c.Cars).Query().IgnoreQueryFilters().Load();