是的,使用拦截器与 EF Core 一起工作是可能的并且效果很好:
https://docs.microsoft.com/en-us/ef/core/logging-events-diagnostics/interceptors
using Microsoft.EntityFrameworkCore.Diagnostics;
using System.Data.Common;
namespace <blah>;
public class RowLevelSecuritySqlInterceptor : DbCommandInterceptor, IRowLevelSecuritySqlInterceptor
{
public Guid? TenantId { get; set; }
public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
SetSessionContext(command);
return base.ReaderExecuting(command, eventData, result);
}
public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result,
CancellationToken cancellationToken = new ())
{
SetSessionContext(command);
return base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
}
public override InterceptionResult<int> NonQueryExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<int> result)
{
SetSessionContext(command);
return base.NonQueryExecuting(command, eventData, result);
}
public override ValueTask<InterceptionResult<int>> NonQueryExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = new ())
{
SetSessionContext(command);
return base.NonQueryExecutingAsync(command, eventData, result, cancellationToken);
}
public override InterceptionResult<object> ScalarExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<object> result)
{
SetSessionContext(command);
return base.ScalarExecuting(command, eventData, result);
}
public override ValueTask<InterceptionResult<object>> ScalarExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<object> result,
CancellationToken cancellationToken = new ())
{
SetSessionContext(command);
return base.ScalarExecutingAsync(command, eventData, result, cancellationToken);
}
private void SetSessionContext(DbCommand command)
{
var tenantId = TenantId is null ? "null" : $"'{TenantId.Value}'";
command.CommandText = $"EXEC sp_set_session_context @key=N'TenantId', @value={tenantId};" + command.CommandText;
}
}
您可以为 DI 创建一个接口:
using Microsoft.EntityFrameworkCore.Diagnostics;
namespace <blah>;
public interface IRowLevelSecuritySqlInterceptor : IDbCommandInterceptor
{
Guid? TenantId { get; set; }
}
并将其注入 DI 容器:
services.TryAddTransient<IRowLevelSecuritySqlInterceptor, RowLevelSecuritySqlInterceptor>();
您的 DbContext 可能类似于:
public partial class MyDbContext
{
private readonly IRowLevelSecuritySqlInterceptor _rowLevelSecuritySqlInterceptor;
...
public AccountServiceDbContext(
...,
IRowLevelSecuritySqlInterceptor rowLevelSecuritySqlInterceptor) : base(options)
{
...,
_rowLevelSecuritySqlInterceptor = rowLevelSecuritySqlInterceptor;
}
...
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(...)
.AddInterceptors(_rowLevelSecuritySqlInterceptor, ... and other interceptors);
}
}