ITenantStore<TKey> interface
Namespace: Tenantry · Package: Tenantry.Core · API reference
Persists and retrieves tenant definitions. Implement this interface to back tenants with a database, configuration file, or any other store.
A custom store registered via UseStore<TStore>() is scoped, and Tenantry resolves it per operation from a fresh dependency-injection scope (so singleton/background services can read tenants without capturing it). Implementations may therefore depend on scoped services such as a DbContext, but must not assume a singleton lifetime or cache scope-bound state across calls.
Return every tenant that exists, suspended or inactive ones included, from both methods: tools that maintain each tenant's database, such as migrations, find tenants here. Decide whether a tenant may be served elsewhere: with an access validator for HTTP requests, and in your own code for background work.
public interface ITenantStore<TKey> where TKey : IEquatable<TKey>, IParsable<TKey>Type parameters
TKey: The tenant identifier type. SeeITenantDescriptor<TKey>for constraints.
Derived types: InMemoryTenantStore<TKey>.
Methods
FindByIdentifierAsync(string, CancellationToken)
Returns the tenant a request's identifier names, or null if it names none. An identifier is what a resolver reads from a request: the tenant's id, or a name your store maps to a tenant, such as a subdomain (acme), a slug in a route or a custom domain (app.acme.com).
ValueTask<ITenantDescriptor<TKey>?> FindByIdentifierAsync(string identifier, CancellationToken cancellationToken = default)Parameters:
identifierstring: The identifier, as the resolver returned it.cancellationTokenCancellationToken: Cancels the lookup.
Returns: ValueTask<ITenantDescriptor<TKey>>
By default the identifier is the tenant's id: it is parsed as TKey with the invariant culture and looked up with ITenantStore<TKey>.GetTenantAsync, and an identifier that does not parse, or parses to the key type's default (Empty, 0) or an empty string, names no tenant. Implement it to resolve tenants by another name, for example a Guid-keyed store whose tenants have slugs. A store that wraps another (to log, say) must forward it to the inner store: otherwise it gets this default, which never reaches the inner store's own mapping.
public async ValueTask<ITenantDescriptor<Guid>?> FindByIdentifierAsync(string identifier, CancellationToken ct) =>
await db.Tenants.SingleOrDefaultAsync(t => t.Slug == identifier, ct);GetAllTenantsAsync(CancellationToken)
Returns every tenant that exists, suspended or inactive ones included.
ValueTask<IReadOnlyList<ITenantDescriptor<TKey>>> GetAllTenantsAsync(CancellationToken cancellationToken = default)Parameters:
cancellationTokenCancellationToken: Cancels the lookup.
Returns: ValueTask<IReadOnlyList<ITenantDescriptor<TKey>>>
GetTenantAsync(TKey, CancellationToken)
Returns the tenant with the given tenantId, or null if no matching tenant exists.
ValueTask<ITenantDescriptor<TKey>?> GetTenantAsync(TKey tenantId, CancellationToken cancellationToken = default)Parameters:
tenantIdTKey: The identifier of the tenant to find.cancellationTokenCancellationToken: Cancels the lookup.
Returns: ValueTask<ITenantDescriptor<TKey>>