Architecture
TenantGuard reads the model an app already builds and never changes it. Three packages work
at three moments: the analyzers when the code compiles, the runtime package when EF Core
builds the model and on every SaveChanges, and the test kit in the app’s tests. A fourth,
the validate tool, runs the model rules in CI without a database, and two test adapters
make each probe row a test of its own in xUnit v3 or TUnit.
- Packages: the six packages, what each depends on, their namespaces, the rings inside the runtime package, the public API, and the repository’s layout.
- Flows: what happens at build, when the model is built, on
SaveChanges, and when the isolation probe runs. - Decisions: why each of these is the way it is.
Vocabulary
Section titled “Vocabulary”- Tenant property: the property that holds the tenant key on an entity, CLR or shadow, found as the rules reference describes.
- Tenant-scoped entity: an entity type whose root type has a tenant property.
- Shared entity: an entity type declared tenant-free on purpose, such as a lookup table
(
Shared<T>()). - Current tenant: the value the app says the context operates as, from the
CurrentTenantresolver. - Guard: the write guard (the SaveChanges interceptor) and the model rules together.
Quality goals
Section titled “Quality goals”- Model validation adds under 50 ms to model building for a 200-entity model.
- The write guard adds no allocations on a
SaveChangeswhen the model has no tenant-scoped entity type, nothing is tracked, or a bypass is open, and O(entries) otherwise (0013). - Every message is deterministic and stable: messages are part of the public contract (Compatibility).
- No reflection over user types at query time: per-model metadata is computed once and
cached on the
IModel.