Introduction
EF Core has the HasQueryFilter extension method, which is a LINQ query predicate applied to EF Core entity models. This is especially useful in scenarios requiring soft deletion. A developer may need to view data without a filter, which is done using IgnoreQueryFilters extension method.
Learn how
- Filter on properties rather than soft deletes and archiving, see the following for soft deletes.
- Control filters using settings from appsettings.json
{
"ContextOptions": {
"UseAuditInterceptor": true,
"CustomersOptions": {
"UseQueryFilter": true,
"CountryCode": 20
},
"CategoryOptions": {
"UseQueryFilter": true,
"Id": 2
}
}
}
Example 1
Using a customer table in a database, filter by country code.
We can hard-code a country code in the OnModelCreating method of the dbContext.
modelBuilder.Entity<Customer>()
.HasQueryFilter(c =>
EF.Property<int?>(c,
nameof(Models.Customer.CountryIdentifier)) == 20); // 20 is the CountryIdentifier for USA in Northwind database
Or use a bool and int properties from appsettings.json.
Below HasQueryFilter is wrapped with an if condition to use HasQueryFilter or not.
if (ContextSettings.Instance.CustomerOptions.UseQueryFilter)
{
modelBuilder.Entity<Customer>()
.HasQueryFilter(c =>
EF.Property<int?>(c,
nameof(Models.Customer.CountryIdentifier)) ==
ContextSettings.Instance.CustomerOptions.CountryCode);
}
public class CustomerOptions
{
public bool UseQueryFilter { get; set; }
public int CountryCode { get; set; }
}
public sealed class ContextSettings
{
private static readonly Lazy<ContextSettings> Lazy = new Lazy<ContextSettings>(() => new ContextSettings());
public static ContextSettings Instance => ContextSettings.Lazy.Value;
public CustomerOptions CustomerOptions { get; set; }
private ContextSettings()
{
// Config is set up in the project file to bind the configuration
// to the ContextOptions class, so we can directly retrieve it here.
ContextOptions options = Config.Configuration.JsonRoot()
.GetSection(nameof(ContextOptions))
.Get<ContextOptions>()!;
CustomerOptions = options.CustomersOptions;
}
}
Example 2
Uses the same logic as in example 1, this time to use HasQueryFilter for filtering on a category for products.
if (ContextSettings.Instance.CategoryOptions.UseQueryFilter)
{
modelBuilder.Entity<Category>()
.HasQueryFilter(c =>
EF.Property<int?>(c,
nameof(Models.Category.CategoryId)) ==
ContextSettings.Instance.CategoryOptions.Id);
}
Another option, perform a contains for multiple category identifiers.
appsettings.json
if (ContextSettings.Instance.CategoryOptions.UseQueryFilter)
{
var ids = ContextSettings.Instance.CategoryOptions.Identifiers;
modelBuilder.Entity<Category>()
.HasQueryFilter(c =>
ids.Contains(EF.Property<int>(c, nameof(Models.Category.CategoryId))));
}
Sample code uses a conditional to try out both.
#if UseThis
if (ContextSettings.Instance.CategoryOptions.UseQueryFilter)
{
modelBuilder.Entity<Category>()
.HasQueryFilter(c =>
EF.Property<int?>(c,
nameof(Models.Category.CategoryId)) ==
ContextSettings.Instance.CategoryOptions.Id);
}
#else
if (ContextSettings.Instance.CategoryOptions.UseQueryFilter)
{
var ids = ContextSettings.Instance.CategoryOptions.Identifiers;
modelBuilder.Entity<Category>()
.HasQueryFilter(c =>
ids.Contains(EF.Property<int>(c, nameof(Models.Category.CategoryId))));
}
#endif
EF Core 10: Using multiple query filters
Introduces named query filters, which allow you to manage each filter separately, including selectively disabling one but not the other.
modelBuilder.Entity<Blog>()
.HasQueryFilter("SoftDeletionFilter", b => !b.IsDeleted)
.HasQueryFilter("TenantFilter", b => b.TenantId == tenantId);
Summary
This article has provided some alternative uses of global filtering in EF Core beyond what most web examples offer, such as soft deletes and the ability to control filter enabling without recompiling the application.



Top comments (0)