I'm trying to implement an ef core global query filter with enable disable conditions. According to my scenario, I have a flag called 'FilterStatus' and its value changes to every user. So any user logged in to the system FilterStatus value is changed. I want to implement a global query filter to filter data according to the FilterStatus flag.
I've come up with the following but does not work if-else statement of global query filter:
public partial class TableDbContext : DbContext, IDbContext
{
protected Guid TenantId { get; set; }
protected UserClaimFilter UserClaimFilter { get; set; } = new UserClaimFilter();
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var tenant = _httpContextAccessor?.HttpContext?.GetTenant();
if (tenant != null)
{
TenantId = Guid.Parse(tenant.Id);
var userClaims =
_httpContextAccessor.HttpContext.Session.GetObject<UserClaimFilter>
(DefaultConstants
.SessionUserClaimStore);
if (userClaims != null)
{
UserClaimFilter = userClaims;
}
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Region>(entity =>
{
if (UserClaimFilter.FilterStatus && UserClaimFilter.RegionClaims.Any())
{
entity.HasQueryFilter(a => a.TenantId == TenantId &&
UserClaimFilter.RegionClaims.Contains(a.RegionCode));
}
else
{
entity.HasQueryFilter(a => a.TenantId == TenantId);
}
});
}
}
In the above code always run the else part even if the UserClaimFilter.FilterStatus is true. I think because of the first time running the OnModelCreating default value of UserClaimFilter.FilterStatus is false.
Any solution to enable-disable global query filter according to the configuration value?
CodePudding user response:
All the dynamic parts of the global query filter conditions must (1) originate from DbContext
members (as with your code) and (2) be part of the expression rather than evaluated once when building the expression. EF Core has mechanism for eliminating the constant true or false parts of conditional expressions when translating the query, so all you need is to include them in the expression. e.g.
entity.HasQueryFilter(a => a.TenantId == TenantId &&
(!UserClaimFilter.FilterStatus ||
!UserClaimFilter.RegionClaims.Any() ||
UserClaimFilter.RegionClaims.Contains(a.RegionCode))
);
When applying the filter to a query, if !UserClaimFilter.FilterStatus
or !UserClaimFilter.RegionClaims.Any()
is true at that time, then the whole expression (a || b || c)
will be considered constant true and eliminated from the generated query (because (x && true) == x
).
Update: As mentioned in the comments, currently EF Core does not fully eliminate the whole expression. a || b
are replaced with bool
parameter having either true
or false
value. But at least c
(which is the actual filter, UserClaimFilter.RegionClaims.Contains(a.RegionCode)
in your case) is eliminated when the bool
parameter value is true
.