Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using EF Core HasQueryFilter on navigation properties

I'm trying to apply a filter to my queries for multi-tenancy but it doesn't allow me to apply the filter when the property is part of a navigation property:

modelBuilder.Entity<Level>().HasQueryFilter(lvl => lvl.SchoolYear.TenantId == _tenantProvider.TenantId);

Here, Level is the property I want filtered but the property to use for filtering which is TenantId is is inside Level.SchoolYear.

If its a top-level property it works fine. How can I apply a global filter when the property I need for filtering is a navigation property?

like image 307
g_b Avatar asked Nov 25 '17 08:11

g_b


2 Answers

As said, these model level filters are not (yet) implemented for navigation properties. Which is sad, because people may not be aware of this restriction and count on it in a multi-tenant architecture. Everybody knows that making errors in a multi-tenant application may be the death of your company, so it's pretty vital to do this right.

Once you've chosen for tenant separation by TenantId (and not the safer schema-per-tenant or database-per-tenant), and knowing this current restriction in EF-core, you may want to build in a safeguard that at least you'll never mix up tenant data when saving changes.

This safeguard could be implemented by defining an interface:

public interface IHasTenantId
{
    int TenantId { get; }   
}

... to be implemented by each entity having a TenantId.

Then in an override of SaveChanges in the DbContext subclass there can be a check if all TenantIds in a change set are identical:

public override int SaveChanges()
{
    var distinctTenantIdsCount = this.ChangeTracker.Entries<IHasTenantId>()
                                     .Select(e => e.Entity.TenantId)
                                     .Distinct().Count();
    if(distinctTenantIdsCount > 1)
    {
        // Throw an exception and handle it.
    }
    return base.SaveChanges();
}
like image 50
Gert Arnold Avatar answered Oct 05 '22 15:10

Gert Arnold


You can't. It's a documented limitation of the current (as of EF Core 2.0.1) Global Query Filters implementation:

Filters cannot contain references to navigation properties.

It's tracked by #8881: Allow to use navigations in model level entity filters enhancement request, but unfortunately has no concrete milestone assigned, which means we don't know if and when it will be fixed.

like image 26
Ivan Stoev Avatar answered Oct 05 '22 13:10

Ivan Stoev