TenantContextGuard class
Namespace: Tenantry.EfCore · Package: Tenantry.EfCore · API reference
An extension point: for code that extends the package, such as another package that builds on it. An application rarely needs it.
An interceptor that checks a context before it opens a connection, before every command it runs and before SaveChanges, so a context that would reach the wrong tenant's data throws instead. Derive from it to fail closed on a condition of your own, such as a context whose schema is not the current tenant's.
Add it to a context's options with AddInterceptors, or from an ITenantDbContextOptionsContributor. EF Core raises no event for a connection that is already open, so a context opened under one tenant and then used under another is caught at its next command. SaveChanges is checked before it starts, because EF Core wraps an exception thrown while a save runs its commands in a DbUpdateException. A command EF Core runs without a context (a HiLo sequence fetch) is checked against the context that opened its connection.
In a context that uses UseTenantry(), the check runs before Tenantry stamps the save's new entities with the current tenant, wherever the guard is among the context's interceptors, so a save it refuses leaves the tracked entities as they were. Interceptors of your own run in the order they were added: add the guard before one that changes entities when a save starts.
Throw TenantNotResolvedException when the context needs a tenant and none is current, and TenantIsolationViolationException when it would use another tenant's data.
[EditorBrowsable(EditorBrowsableState.Advanced)]
public abstract class TenantContextGuard : IDbConnectionInterceptor, IDbCommandInterceptor, ISaveChangesInterceptor, IInterceptorImplements IDbConnectionInterceptor, IDbCommandInterceptor, ISaveChangesInterceptor, IInterceptor.
Methods
Check(DbContext)
Throws when context must not touch the database now.
protected abstract void Check(DbContext context)Parameters:
contextDbContext: The context about to open a connection, run a command or save.
ConnectionOpening(DbConnection, ConnectionEventData, InterceptionResult)
Called just before EF intends to call Open().
public virtual InterceptionResult ConnectionOpening(DbConnection connection, ConnectionEventData eventData, InterceptionResult result)Parameters:
connectionDbConnection: The connection.eventDataConnectionEventData: Contextual information about the connection.resultInterceptionResult: Represents the current result if one exists. This value will haveIsSuppressedset to true if some previous interceptor suppressed execution by callingSuppress(). This value is typically used as the return value for the implementation of this method.
Returns: InterceptionResult: If IsSuppressed is false, then EF will continue as normal. If IsSuppressed is true, then EF will suppress the operation it was about to perform. An implementation of this method for any interceptor that is not attempting to suppress the operation is to return the result value passed in.
ConnectionOpeningAsync(DbConnection, ConnectionEventData, InterceptionResult, CancellationToken)
Called just before EF intends to call OpenAsync().
public virtual ValueTask<InterceptionResult> ConnectionOpeningAsync(DbConnection connection, ConnectionEventData eventData, InterceptionResult result, CancellationToken cancellationToken = default)Parameters:
connectionDbConnection: The connection.eventDataConnectionEventData: Contextual information about the connection.resultInterceptionResult: Represents the current result if one exists. This value will haveIsSuppressedset to true if some previous interceptor suppressed execution by callingSuppress(). This value is typically used as the return value for the implementation of this method.cancellationTokenCancellationToken: ACancellationTokento observe while waiting for the task to complete.
Returns: ValueTask<InterceptionResult>: If IsSuppressed is false, then EF will continue as normal. If IsSuppressed is true, then EF will suppress the operation it was about to perform. An implementation of this method for any interceptor that is not attempting to suppress the operation is to return the result value passed in.
Exceptions:
OperationCanceledException: If theCancellationTokenis canceled.
FindContext(DbConnection?)
The context that owns connection, for an event that names none. By default, the last context that opened it.
protected virtual DbContext? FindContext(DbConnection? connection)Parameters:
connectionDbConnection: The connection.
Returns: DbContext: The context, or null when none is known.
NonQueryExecuting(DbCommand, CommandEventData, InterceptionResult<int>)
Called just before EF intends to call ExecuteNonQuery().
public InterceptionResult<int> NonQueryExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<int> result)Parameters:
commandDbCommand: The command.eventDataCommandEventData: Contextual information about the command and execution.resultInterceptionResult<int>: Represents the current result if one exists. This value will haveHasResultset to true if some previous interceptor suppressed execution by callingSuppressWithResult(TResult). This value is typically used as the return value for the implementation of this method.
Returns: InterceptionResult<int>: If HasResult is false, the EF will continue as normal. If HasResult is true, then EF will suppress the operation it was about to perform and use Result instead. An implementation of this method for any interceptor that is not attempting to change the result is to return the result value passed in.
NonQueryExecutingAsync(DbCommand, CommandEventData, InterceptionResult<int>, CancellationToken)
Called just before EF intends to call ExecuteNonQueryAsync().
public ValueTask<InterceptionResult<int>> NonQueryExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)Parameters:
commandDbCommand: The command.eventDataCommandEventData: Contextual information about the command and execution.resultInterceptionResult<int>: Represents the current result if one exists. This value will haveHasResultset to true if some previous interceptor suppressed execution by callingSuppressWithResult(TResult). This value is typically used as the return value for the implementation of this method.cancellationTokenCancellationToken: ACancellationTokento observe while waiting for the task to complete.
Returns: ValueTask<InterceptionResult<int>>: If HasResult is false, the EF will continue as normal. If HasResult is true, then EF will suppress the operation it was about to perform and use Result instead. An implementation of this method for any interceptor that is not attempting to change the result is to return the result value passed in, often using FromResult<TResult>(TResult)
Exceptions:
OperationCanceledException: If theCancellationTokenis canceled.
Opening(DbConnection, ConnectionEventData)
The context a connection event belongs to, recorded for the connection's later commands.
protected DbContext? Opening(DbConnection connection, ConnectionEventData eventData)Parameters:
connectionDbConnection: The connection.eventDataConnectionEventData: The event.
Returns: DbContext: The context, or null when none is known.
ReaderExecuting(DbCommand, CommandEventData, InterceptionResult<DbDataReader>)
Called just before EF intends to call ExecuteReader().
public InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)Parameters:
commandDbCommand: The command.eventDataCommandEventData: Contextual information about the command and execution.resultInterceptionResult<DbDataReader>: Represents the current result if one exists. This value will haveHasResultset to true if some previous interceptor suppressed execution by callingSuppressWithResult(TResult). This value is typically used as the return value for the implementation of this method.
Returns: InterceptionResult<DbDataReader>: If HasResult is false, the EF will continue as normal. If HasResult is true, then EF will suppress the operation it was about to perform and use Result instead. An implementation of this method for any interceptor that is not attempting to change the result is to return the result value passed in.
ReaderExecutingAsync(DbCommand, CommandEventData, InterceptionResult<DbDataReader>, CancellationToken)
Called just before EF intends to call ExecuteReaderAsync().
public ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result, CancellationToken cancellationToken = default)Parameters:
commandDbCommand: The command.eventDataCommandEventData: Contextual information about the command and execution.resultInterceptionResult<DbDataReader>: Represents the current result if one exists. This value will haveHasResultset to true if some previous interceptor suppressed execution by callingSuppressWithResult(TResult). This value is typically used as the return value for the implementation of this method.cancellationTokenCancellationToken: ACancellationTokento observe while waiting for the task to complete.
Returns: ValueTask<InterceptionResult<DbDataReader>>: If HasResult is false, the EF will continue as normal. If HasResult is true, then EF will suppress the operation it was about to perform and use Result instead. An implementation of this method for any interceptor that is not attempting to change the result is to return the result value passed in, often using FromResult<TResult>(TResult)
Exceptions:
OperationCanceledException: If theCancellationTokenis canceled.
SavingChanges(DbContextEventData, InterceptionResult<int>)
Called at the start of <see cref="O:DbContext.SaveChanges"></see>.
public virtual InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)Parameters:
eventDataDbContextEventData: Contextual information about theDbContextbeing used.resultInterceptionResult<int>: Represents the current result if one exists. This value will haveHasResultset to true if some previous interceptor suppressed execution by callingSuppressWithResult(TResult). This value is typically used as the return value for the implementation of this method.
Returns: InterceptionResult<int>: If HasResult is false, the EF will continue as normal. If HasResult is true, then EF will suppress the operation it was about to perform and use Result instead. An implementation of this method for any interceptor that is not attempting to change the result is to return the result value passed in.
SavingChangesAsync(DbContextEventData, InterceptionResult<int>, CancellationToken)
Called at the start of <see cref="O:DbContext.SaveChangesAsync"></see>.
public virtual ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)Parameters:
eventDataDbContextEventData: Contextual information about theDbContextbeing used.resultInterceptionResult<int>: Represents the current result if one exists. This value will haveHasResultset to true if some previous interceptor suppressed execution by callingSuppressWithResult(TResult). This value is typically used as the return value for the implementation of this method.cancellationTokenCancellationToken: ACancellationTokento observe while waiting for the task to complete.
Returns: ValueTask<InterceptionResult<int>>: If HasResult is false, the EF will continue as normal. If HasResult is true, then EF will suppress the operation it was about to perform and use Result instead. An implementation of this method for any interceptor that is not attempting to change the result is to return the result value passed in.
Exceptions:
OperationCanceledException: If theCancellationTokenis canceled.
ScalarExecuting(DbCommand, CommandEventData, InterceptionResult<object>)
Called just before EF intends to call ExecuteScalar().
public InterceptionResult<object> ScalarExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<object> result)Parameters:
commandDbCommand: The command.eventDataCommandEventData: Contextual information about the command and execution.resultInterceptionResult<object>: Represents the current result if one exists. This value will haveHasResultset to true if some previous interceptor suppressed execution by callingSuppressWithResult(TResult). This value is typically used as the return value for the implementation of this method.
Returns: InterceptionResult<object>: If HasResult is false, the EF will continue as normal. If HasResult is true, then EF will suppress the operation it was about to perform and use Result instead. An implementation of this method for any interceptor that is not attempting to change the result is to return the result value passed in.
ScalarExecutingAsync(DbCommand, CommandEventData, InterceptionResult<object>, CancellationToken)
Called just before EF intends to call ExecuteScalarAsync().
public ValueTask<InterceptionResult<object>> ScalarExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<object> result, CancellationToken cancellationToken = default)Parameters:
commandDbCommand: The command.eventDataCommandEventData: Contextual information about the command and execution.resultInterceptionResult<object>: Represents the current result if one exists. This value will haveHasResultset to true if some previous interceptor suppressed execution by callingSuppressWithResult(TResult). This value is typically used as the return value for the implementation of this method.cancellationTokenCancellationToken: ACancellationTokento observe while waiting for the task to complete.
Returns: ValueTask<InterceptionResult<object>>: If HasResult is false, the EF will continue as normal. If HasResult is true, then EF will suppress the operation it was about to perform and use Result instead. An implementation of this method for any interceptor that is not attempting to change the result is to return the result value passed in, often using FromResult<TResult>(TResult)
Exceptions:
OperationCanceledException: If theCancellationTokenis canceled.
SharedAcrossTenantsAttribute class
Marks an entity type whose rows every tenant shares, such as a country list or the tenant table itself, so TenantModel.FindUnisolatedEntityTypestenantry-efcore-tenantmodel.md does not report it.
TenantIsolationViolationException class
Thrown when EF Core would read or write across tenants.