When working with databases, it’s common to need consistent filtering across multiple queries.
Without a built-in solution, we would have to manually apply filters in every query, increasing the risk of inconsistencies and missing filters.
This repetitive logic not only adds complexity but also makes the code harder to maintain.
To address this, Entity Framework Core introduced Query Filters.
Global Query Filters
Query Filters allow us to define global filtering rules at the entity level, ensuring they are consistently applied across all queries:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.HasQueryFilter(product => !product.IsDeleted);
}
Basically, EF adds a WHERE whenever entity is queried.
This worked well, but with one big limitation: You could only define one query filter per entity.
If you needed multiple filters, you had to chain them together using && inside a single expression.
That’s fine for a few filters, but if you wanted to temporarily disable just one, you had to turn off all filters and reapply the rest manually.
To learn more about global query filters, check out my blog post on them: Apply Global Query Filters in EF Core
Named Query Filters
With EF Core 10, this limitation is gone.
The new Named Query Filters feature allows you to attach multiple filters to the same entity, each identified by a unique name:
builder.HasQueryFilter(
FilterNames.SoftDelete,
product => !product.IsDeleted);
Assigning Multiple Query Filters
Previously to assign multiple filters looked like this:
builder.HasQueryFilter(product => !product.IsDeleted && product.Quantity > 0);
Now you can simply use multiple HasQueryFilter methods:
builder.HasQueryFilter(
FilterNames.SoftDelete,
product => !product.IsDeleted);
builder.HasQueryFilter(
FilterNames.InStock,
product => product.Quantity > 0);
Before EF Core 10, if you tried defining multiple HasQueryFilter calls like this, only the last one would take effect.
Ignore Query Filters
The IgnoreQueryFilters method also got an upgrade, you can now enable or disable filters individually:
app.MapGet("products/ignore-all-filters", async (ApplicationDbContext dbContext, CancellationToken cancellationToken) =>
await dbContext.Products
.IgnoreQueryFilters()
.ToListAsync(cancellationToken));
app.MapGet("products/ignore-sof-tdelete-filter", async (ApplicationDbContext dbContext, CancellationToken cancellationToken) =>
await dbContext.Products
.IgnoreQueryFilters([FilterNames.SoftDelete])
.ToListAsync(cancellationToken));
app.MapGet("products/ignore-in-stock-filter", async (ApplicationDbContext dbContext, CancellationToken cancellationToken) =>
await dbContext.Products
.IgnoreQueryFilters([FilterNames.InStock])
.ToListAsync(cancellationToken));
app.MapGet("products/all-filters", async (ApplicationDbContext dbContext, CancellationToken cancellationToken) =>
await dbContext.Products
.ToListAsync(cancellationToken));
It now accepts an array of filter names, allowing you to ignore only specific filters when querying data.
Conclusion
Consistent filtering across multiple queries is essential when working with databases.
Entity Framework Core's Query Filters provide a simple and effective solution to this problem by allowing global filtering rules to be applied at the entity level.
By using query filters, you can automatically exclude soft-deleted records, enforce multi-tenancy and apply other common conditions.
EF Core also offers the flexibility to disable query filters for specific queries when needed, using IgnoreQueryFilters.
If you want to check out examples I created, you can find the source code here:
Source CodeI hope you enjoyed it, subscribe and get a notification when a new blog is up!
