-1

我们使用 asp.net core 3.x 和 EF Core 3.x

我们确实对几个实体有授权(因此它只允许表中的少数记录作为响应返回),这是通过访问 SQL 视图(内部连接两个表)实现的,我们针对该视图进行查询,该视图将只为您提供那些记录哪个登录用户被授权。

为了实现这一点,我们需要@@spid在执行选择查询(在应用程序表上)之前将登录的用户 ID 和(SQL Server)插入表(​​会话)(在上面的视图中使用),并且我们需要在之后立即删除该记录查询被执行。为了实现这一点,我们正在使用DbInterceptor.

会话表:

用户身份 会话 ID
1 32
2 26

应用表:

ID 用户身份 文本
1 1 我需要帮助...
2 2 我不会说英文...

db拦截器实现:

public class DbInterceptor : DbCommandInterceptor
{
    private readonly IExecutionContext _executionContext;

    public DbInterceptor(IExecutionContext executionContext)
    {
        _executionContext = executionContext;
    }

    public override async Task<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command,
        CommandEventData eventData, InterceptionResult<DbDataReader> result,
        CancellationToken cancellationToken = new CancellationToken())
    {
        var sqlParameter = new SqlParameter("UserId",
            _executionContext.CurrentPrincipal.FindFirst(Claims.TSSUserId).Value);
        await eventData.Context.Database.ExecuteSqlRawAsync("EXEC InsertUserSP @UserId", sqlParameter);
        return await base.ReaderExecutingAsync(command, eventData, result);
    }

    public override async Task<DbDataReader> ReaderExecutedAsync(DbCommand command,
        CommandExecutedEventData eventData, DbDataReader result,
        CancellationToken cancellationToken = new CancellationToken())
    {
        var sqlParameter = new SqlParameter("UserId",
            _executionContext.CurrentPrincipal.FindFirst(Claims.TSSUserId).Value);
        await eventData.Context.Database.ExecuteSqlRawAsync("EXEC DeleteUserSP @UserId", sqlParameter);

        return await base.ReaderExecutedAsync(command, eventData, result);
    }
}

现在有了这个我们有一个例外

System.InvalidOperationException: '已经有一个打开的 DataReader 与此命令关联,必须先关闭。' 在线等待 eventData.Context.Database.ExecuteSqlRawAsync("EXEC DeleteUserSP @UserId", sqlParameter); 在拦截器的 `ReaderExecutedAsync` 方法中。

我用谷歌搜索了这个异常,发现可以通过MultipleActiveResultSets在连接字符串中提供 true 来克服这个错误。

使用有副作用MultipleActiveResultSets吗?

在浏览该主题时,我遇到了几篇文章,指出它可以在不同的请求之间共享连接实例,何时MultipleActiveResultSets设置为 true。如果在实时请求线程之间共享相同的连接,那么它可能会出现问题,因为授权正在处理这样一个事实,即它@@spid对于所有正在运行的实时线程都具有唯一性。

DbContext连接池中的连接实例将如何提供?

4

1 回答 1

1

ReaderExecutedAsync数据读取器仍然打开并获取行。所以现在取消设置用户还为时过早。尝试钩住DataReaderDisposing

如果这不起作用,请强制打开连接并在拦截器外部调用该过程。例如

var con = db.Database.GetDbConnection();
await con.OpenAsync();
await con.Database.ExecuteSqlRawAsync("EXEC InsertUserSP @UserId", sqlParameter);

这将确保在 Disposed DbContext 之前,连接不会返回到连接池。

于 2021-02-12T13:43:05.960 回答