EF Core Filters
Tags: csharp, efcore, dotnet
Published: 2021-06-15 08:29:36

---
title: "EF Core Filters"
published: true
description: Wouldn't it be nice if you could turn off filters, one by one, or adjust them at run time. Well, you can, kinda. You just have to remember, that we can't really turn the filters off, so we have to adjust our filter to get the behaviour we want.
canonical_url: https://blog.lxalln.com/2021/06/ef-core-filters/
tags:
  - csharp
  - efcore
  - dotnet
---

Entity Framework core supports [Global Query Filter](https://docs.microsoft.com/en-us/ef/core/querying/filters) which are a great way to enforce part of a query globally through your app.

Unfortunately, it comes with this note:

> It is currently not possible to define multiple query filters on the same entity - only the last one will be applied. However, you can define a single filter with multiple conditions using the logical AND operator (&& in C#).

So, because you have to combine your filters into 1 big filter, this means that turning filters off using `.IgnoreQueryFilters()` turns off all filters. Which is a little dangerous, if you ask me.

### Controlling filters 1 by 1
Wouldn't it be nice if you could turn off filters, one by one, or adjust them at run time. Well, you can, kinda. You just have to remember, that we can't really turn the filters off, so we have to adjust our filter to get the behaviour we want.


#### Multi-tenanted
We'll start with a simple tenanting filter. This is common across a lot of Saas apps:

In this example, we are building an Ordering system for multiple tenants.

```cs
///... order map
builder.HasQueryFilter(o => o.TenantId == DbContext.TenantId);
```

All good so far. How your `TenantId` is set on the db context, is beyond the scope of this post, but it will likely be setup early in your request pipeline.

At runtime, we can control this `TenantId` and override it. Your reasons for doing this are your own, I'm not going to judge you!

A typical query might look like this:
```cs
var latestOrders = await _dbContext.Orders
                                   .OrderByDescending(o => o.CreatedOn)
                                   .Take(10)
                                   .ToListAsync();                          
```

We can control the tenant Id at runtime, in the controller, and scope it with a `using` block:

```csharp
using(_dbContext.OverrideTenantIdFilter(newTenantId))
{
    var latestOrders = await _dbContext.Orders
                                       .OrderByDescending(o => o.CreatedOn)
                                       .Take(10)
                                       .ToListAsync();       
}
```

This will allow us to change the filter that gets used to build the query, but only during our scope. After wards it'll return to it's previous value. Very handy.

We need a couple of things to do this. A method on our `DbContext` and a new class.

First up, we'll create a new class to handle this for us:

```csharp
public class TenantIdFilterOverrider : IDisposable
{
    private readonly AppDbContext _dbContext;
    private readonly Guid? _previousTenantIdOverride;

    public TenantIdFilterOverrider(AppDbContext dbContext, Guid overrideId)
    {
        _dbContext = dbContext;

        _previousTenantIdOverride = _dbContext.TenantIdOverride;

        _dbContext.TenantIdOverride = overrideId;
    }

    public void Dispose()
    {
        _dbContext.TenantIdOverride = _previousTenantIdOverride;
    }
}
```

Then best part about these filters, is the use of the `Dispose()` method, to return the context back to its previous state when we are done with it.

Next we'll add a method to our `DbContext` that let's us use this:


```csharp
public TenantIdFilterOverrider OverrideTenantIdFilter(Guid tenantId)
{
    return new TenantIdFilterOverrider(this, tenantId);
}
```

We'll take a quick look at how the `TenantId` property works:

```csharp
internal Guid? TenantIdOverride { get; set; }

internal Guid TenantId
{
    get
    {
        var tenantId = TenantIdOverride ?? _initialTenantId;

        return tenantId.Value;
    }
}
```

This allows control over the tenant Id in a scoped way.


#### Turning a filter off
The above example is great, when you _always_ have a value as part of your filter, but sometimes you might want to turn a filter off.

For example, we may have a second filter as part of our orders filter. Imagine we have a flag against the database to control visibilty of deleted orders:

```csharp
builder.HasQueryFilter(o => o.TenantId == DbContext.TenantId
                            && DbContext.DeletedFilter.Contains(o.IsDeleted));
```

Now, this is written in a slightly perculiar way. Remember that I said you cannot really turn the filters off? Well, in order to get a performant query, we need to support 3 states:
- Deleted (1)
- NotDeleted (0)
- Either (_nothing_)

So, on our `DbContext` we have a new property, `DeletedFilter`:

```csharp
internal IEnumerable<bool> OrderModeFilter
{
    get
    {
        if (DisableDeletedFilter)
        {
            return new []{ false, true };
        }

        return new [] { true };
    }
}
```

When `DisableDeletedFilter` is true, this will produce SQL that is something akin to:

```sql
select * from Orders
where TenantId = '<GUID>'
and IsDeleted in (0, 1)
```

or when the filter is not disabled:

```sql
select * from Orders
where TenantId = '<GUID>'
and IsDeleted in (1)
```

This allows SQL to optimise the query in a performant way, without us having to disable all of our filters, and remember to add back on the `TenantId` filter!

So, just to complete this part, I'll show you how we disable that filter too:

```csharp
//DbContext.cs
internal bool DisableDeletedFilter { get; set; }

public DeletedFilterDisabler IgnoreDeletedFilter()
{
    return new DeletedFilterDisabler(this);
}
```

```csharp
// DeletedFilterDisabler.cs
public class TestModeFilterDisabler : IDisposable
{
    private readonly AppDbContext _dbContext;
    private readonly bool _previousValue;

    public TestModeFilterDisabler(AppDbContext dbContext)
    {
        _dbContext = dbContext;
        _previousValue = _dbContext.DisableTestModeFilter;
        _dbContext.DisableTestModeFilter = true;
    }

    public void Dispose()
    {
        _dbContext.DisableTestModeFilter = _previousValue;
    }
}
```
```csharp
using(_dbContext.IgnoreDeletedFilter())
{
    var latestOrders = await _dbContext.Orders
                                       .OrderByDescending(o => o.CreatedOn)
                                       .Take(10)
                                       .ToListAsync();     
}
```

This works really well with dependency injection, meaning a scoped `DbContext` may have it's tenant, or filters controlled early on in the pipeline, and the lower levels don't need to care about what filters they are running.