Files
work-club-manager/backend/WorkClub.Infrastructure/Data/Interceptors/TenantDbConnectionInterceptor.cs

77 lines
2.7 KiB
C#
Raw Normal View History

using System.Data.Common;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using Npgsql;
namespace WorkClub.Infrastructure.Data.Interceptors;
public class TenantDbConnectionInterceptor : DbCommandInterceptor
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<TenantDbConnectionInterceptor> _logger;
public TenantDbConnectionInterceptor(
IHttpContextAccessor httpContextAccessor,
ILogger<TenantDbConnectionInterceptor> logger)
{
_httpContextAccessor = httpContextAccessor;
_logger = logger;
}
public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(
DbCommand command,
CommandEventData eventData,
InterceptionResult<DbDataReader> result,
CancellationToken cancellationToken = default)
{
SetTenantContext(command);
return base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
}
public override InterceptionResult<DbDataReader> ReaderExecuting(
DbCommand command,
CommandEventData eventData,
InterceptionResult<DbDataReader> result)
{
SetTenantContext(command);
return base.ReaderExecuting(command, eventData, result);
}
public override ValueTask<InterceptionResult<int>> NonQueryExecutingAsync(
DbCommand command,
CommandEventData eventData,
InterceptionResult<int> result,
CancellationToken cancellationToken = default)
{
SetTenantContext(command);
return base.NonQueryExecutingAsync(command, eventData, result, cancellationToken);
}
public override InterceptionResult<int> NonQueryExecuting(
DbCommand command,
CommandEventData eventData,
InterceptionResult<int> result)
{
SetTenantContext(command);
return base.NonQueryExecuting(command, eventData, result);
}
private void SetTenantContext(DbCommand command)
{
var tenantId = _httpContextAccessor.HttpContext?.Items["TenantId"] as string;
if (!string.IsNullOrWhiteSpace(tenantId) && command.Connection is NpgsqlConnection conn)
{
// Prepend SET LOCAL to set tenant context
// Note: EF Core creates implicit transactions for queries, so SET LOCAL will work
command.CommandText = $"SET LOCAL app.current_tenant_id = '{tenantId}'; " + command.CommandText;
_logger.LogInformation("SetTenantContext: Prepended SET LOCAL for tenant {TenantId}", tenantId);
}
else
{
_logger.LogWarning("ReaderExecuting: No tenant context available (tenantId={TenantId})", tenantId ?? "null");
}
}
}