'Entity Framework Core Global Dynamic Query Filter

we are using ef core 3.1 And we want to use dynamic query filter, I tried sample implementation but did not work correctly we expected, filtering always same tenant id,i tried to explain at below


 public class TestDbContext : DbContext
    {
      
        public DbSet<TenantUser> TenantUsers { get; set; }
      

        private readonly ITenantProvider _tenantProvider;

        private Guid? TenantId => _tenantProvider.TenantId;

        public TestDbContext (DbContextOptions<TestDbContext > options, ITenantProvider tenantProvider) : base(options)
        {
               _tenantProvider = tenantProvider;
        }


        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<TenantUser>()
                .HasQueryFilter(p => EF.Property<Guid>(p, "TenantId") == TenantId);
        }

    }

ITenantProvider returns TenantId from HttpContext headers

this code filtering always same tenant id from coming first request

Update:


 public class TenantProvider : ITenantProvider
    {
        private readonly IHttpContextAccessor _httpContextAccessor;

        public TenantProvider(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }

        public Guid? TenantId
        {
            get
            {
                if (_httpContextAccessor.HttpContext.Request.Headers.TryGetValue(HeaderNames.TenantId, out var tenantId) &&
                    Guid.TryParse(tenantId, out Guid parsedTenantId))
                {
                    return parsedTenantId;
                }
                return null;
            }
        }
    }

For example First Request TenantId = 60000000-0000-0000-0000-000000000000 This filter => 60000000-0000-0000-0000-000000000000

Second Request TenantId = 10000000-0000-0000-0000-000000000000 This filter => 60000000-0000-0000-0000-000000000000



Solution 1:[1]

We tried something similar like that a few years ago. Main problem is here that OnModelCreating method only triggered once. So HasQueryFilter works once and gets the current tenant id from provider and it applies to all queries the same tenant id.

Solution 2:[2]

You should also implement a custom IModelCacheKeyFactory

public class MyModelCacheKeyFactory : IModelCacheKeyFactory
{
    public object Create(DbContext context)
    {
        if (context is TestDbContext testDbContext)
        {
            return (context.GetType(), testDbContext.TenantId);
        }
        return context.GetType();
    }
}

And then, you need to replace like this

var builder = new DbContextOptionsBuilder<TestDbContext>();

builder.ReplaceService<IModelCacheKeyFactory, MyModelCacheKeyFactory>();

var context = new TestDbContext(builder.Options);

Reference: https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.infrastructure.imodelcachekeyfactory

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ahmet Selman ?ener
Solution 2 Richard