DKNet

Filter owned rows

Part of DKNet.EfCore.DataAuthorization. In the Quick Start, IDataOwnerDbContext.AccessibleKeys forwards the provider keys. Override the provider to grant several keys:

public sealed class DemoOwner : IDataOwnerProvider
{
    public string? GetOwnershipKey() => "tenant-a";
    public ICollection<string> GetAccessibleKeys() => ["tenant-a", "tenant-b"];
}

DataOwnerAuthQuery checks each IOwnedBy hierarchy root, rejects unfilterable models, and applies the context unrestricted flag or accessible-key membership on each query.

The predicate is IsUnrestrictedAccess || AccessibleKeys.Contains(entity.OwnedBy). Empty keys deny every owned row unless unrestricted access is explicitly enabled. DataOwnerAuthQuery.IsIgnorable is false, so a specification’s IgnoreQueryFilters() does not bypass it.

⚙️ Configuration reference

Setting Type Default Effect
AccessibleKeys IEnumerable<string> Required implementation Keys allowed on reads.
IsUnrestrictedAccess bool false Explicit context-wide read-filter bypass.
GetAccessibleKeys() ICollection<string> One ownership key, or empty for null/empty key Source your context can forward; also used by the save guard.

AddDataOwnerProvider<TDbContext, TProvider>() requires TDbContext : DbContext, IDataOwnerDbContext. It registers a global model builder and scoped provider, then the keyed save hook. Call UseAutoConfigModel<TDbContext>() on options to apply the builder.

🧱 Where it fits

The ownership architecture shows the read and write paths; the audit-field split explains which provider fills identity fields. The filter is installed on hierarchy roots only, covering TPH, TPT and TPC descendants. A derived IOwnedBy type below a root that does not implement IOwnedBy throws during model building: move the contract to the root.

⚠️ Gotchas & limits