Status: ✅ Finalized — Implemented in Dext.Entity.DbSet and Dext.Specifications.SQL.Generator
This architectural specification details the design for dynamically bypassing global query filters (like Multi-Tenancy and Soft Delete) at the query level in Dext ORM.
Dext ORM supports Global Query Filters (such as automatic soft-delete filter IsDeleted = 0 or tenant isolation TenantId = X). While extremely useful for security and convenience, administrative queries or specific reports often need to bypass these filters to access raw historical records or query across tenants.
Bypassing these filters required writing manual raw SQL commands. This specification defined a type-safe fluent mechanism to bypass query filters at the DbSet<T> level dynamically.
The .IgnoreQueryFilters chainable method is available on both IDbSet<T> and TFluentQuery<T>:
// Via DbSet directly
var AllTasks := Db.Tasks.IgnoreQueryFilters.ToList;
// Via Specification
var Spec := TSpecification<TUser>.Create;
Spec.IgnoreQueryFilters;
var AllUsers := Db.Users.ToList(Spec);- State Flag:
TFluentQuery<T>andISpecification<T>maintain a boolean flagFIgnoreQueryFilters(default:False). - Spec Propagation: In
TDbSet<T>.ToList(ASpec), the spec'sIsIgnoringFiltersflag is read and applied to the DbSet's internal state before executing the query. TheResetQueryFlagsin thefinallyblock ensures this is scoped to a single call. - Soft Delete Filter:
TSQLGenerator<T>.GetSoftDeleteFilterreturns an empty string whenFIgnoreQueryFiltersisTrue. - Tenant Filter:
TDbSet<T>.ApplyTenantFilterskips injection whenFIgnoreQueryFiltersisTrue. - Global Query Filters:
TSQLGenerator<T>.GetQueryFiltersSQLexits early whenFIgnoreQueryFiltersisTrue.
The specification pattern supports ignoring query filters explicitly, enabling clean reuse of administrative specs:
type
TAdminUserListSpec = class(TSpecification<TUser>)
public
constructor Create;
end;
constructor TAdminUserListSpec.Create;
begin
inherited Create;
IgnoreQueryFilters; // Bypasses Soft Delete & Tenancy automatically
Where(TPropExpression.Create('Role').Equal('SuperAdmin'));
end;
// Usage
var AllAdmins := Db.Users.ToList(TAdminUserListSpec.Create);| File | Change |
|---|---|
Dext.Entity.DbSet.pas |
ToList(ASpec) now propagates IsIgnoringFilters and IsOnlyDeleted from ISpecification to FIgnoreQueryFilters/FOnlyDeleted before calling ApplyTenantFilter and CreateGenerator. |
Dext.Entity.Query.pas |
TFluentQuery<T>.IgnoreQueryFilters already propagated to ISpecification — no change needed. |
Dext.Specifications.SQL.Generator.pas |
Already respected FIgnoreQueryFilters — no change needed. |
Dext.Entity.DynamicQueryFilter.Tests.pas |
New test file with 4 unit tests and 4 integration tests. |
Dext.Entity.UnitTests.dpr |
Registered TFluentQueryTests, TDynamicQueryFilterUnitTests, and TDynamicQueryFilterIntegrationTests. |
Dext Specifications — S34 Dynamic Query Filters | June 2026 — Finalized