You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core拦截SqlConnection.Open并注入UserID到session_context求助

问题根源

你编写的DbConnectionInterceptor是EF Core专属的拦截器,仅对EF Core创建和管理的数据库连接生效。而你当前的代码是直接使用ADO.NET原生的SqlConnection手动创建连接,完全绕开了EF Core的连接管理流程,所以拦截器根本不会被触发。


解决方案

根据你的代码使用场景,提供三种可行方案:

方案1:改用EF Core执行SQL操作(推荐)

如果可以切换到EF Core执行SQL,拦截器就能正常工作,同时能统一管理连接和用户ID注入。

步骤1:修改拦截器,添加用户ID注入逻辑

using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using System.Security.Claims;
using System.Data.SqlClient;

public class DbLogUserIdInterceptor : DbConnectionInterceptor
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    // 通过依赖注入获取当前请求上下文
    public DbLogUserIdInterceptor(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    // 同步打开连接时注入UserID
    public override InterceptionResult ConnectionOpening(
            DbConnection connection,
            ConnectionEventData eventData,
            InterceptionResult result)
    {
        if (connection is SqlConnection sqlConnection)
        {
            var userId = _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);
            if (!string.IsNullOrEmpty(userId))
            {
                using var command = sqlConnection.CreateCommand();
                command.CommandText = "EXEC sp_set_session_context @key = N'UserID', @value = @userId";
                command.Parameters.AddWithValue("@userId", userId);
                command.ExecuteNonQuery();
            }
        }
        return base.ConnectionOpening(connection, eventData, result);
    }

    // 异步打开连接时注入UserID
    public override async ValueTask<InterceptionResult> ConnectionOpeningAsync(
        DbConnection connection,
        ConnectionEventData eventData,
        InterceptionResult result,
        CancellationToken cancellationToken = default)
    {
        if (connection is SqlConnection sqlConnection)
        {
            var userId = _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);
            if (!string.IsNullOrEmpty(userId))
            {
                using var command = sqlConnection.CreateCommand();
                command.CommandText = "EXEC sp_set_session_context @key = N'UserID', @value = @userId";
                command.Parameters.AddWithValue("@userId", userId);
                await command.ExecuteNonQueryAsync(cancellationToken);
            }
        }
        return await base.ConnectionOpeningAsync(connection, eventData, result, cancellationToken);
    }
}

步骤2:注册服务与拦截器

在Program.cs中注册IHttpContextAccessor和EF Core上下文:

builder.Services.AddHttpContextAccessor();
builder.Services.AddDbContext<Database1DbContext>((serviceProvider, options) =>
{
    options.UseSqlServer(builder.Configuration.GetConnectionString("YourConnectionString"))
           .AddInterceptors(serviceProvider.GetRequiredService<DbLogUserIdInterceptor>());
});

// 注册拦截器本身
builder.Services.AddScoped<DbLogUserIdInterceptor>();

步骤3:用EF Core执行更新操作

替换原ADO.NET代码为EF Core的SQL执行方式:

// 注入Database1DbContext
private readonly Database1DbContext _dbContext;

public YourService(Database1DbContext dbContext)
{
    _dbContext = dbContext;
}

// 执行更新逻辑
public async Task UpdateCustomer(Customer customer)
{
    var sql = "UPDATE Customer SET CompanyName = @CompanyName, EmailAddress = @EmailAddress WHERE CustomerID = @CustomerID";
    await _dbContext.Database.ExecuteSqlRawAsync(sql, 
        new SqlParameter("@CompanyName", customer.CompanyName),
        new SqlParameter("@EmailAddress", customer.EmailAddress),
        new SqlParameter("@CustomerID", customer.CustomerID));
}

方案2:为ADO.NET创建扩展方法(兼容原有代码)

如果必须保留原生ADO.NET代码,可以创建扩展方法,在打开连接时自动注入UserID。

步骤1:编写扩展方法

using System.Data.SqlClient;

public static class SqlConnectionExtensions
{
    public static void OpenWithUserId(this SqlConnection connection, string userId)
    {
        connection.Open();
        if (!string.IsNullOrEmpty(userId))
        {
            using var command = connection.CreateCommand();
            command.CommandText = "EXEC sp_set_session_context @key = N'UserID', @value = @userId";
            command.Parameters.AddWithValue("@userId", userId);
            command.ExecuteNonQuery();
        }
    }

    public static async Task OpenWithUserIdAsync(this SqlConnection connection, string userId, CancellationToken cancellationToken = default)
    {
        await connection.OpenAsync(cancellationToken);
        if (!string.IsNullOrEmpty(userId))
        {
            using var command = connection.CreateCommand();
            command.CommandText = "EXEC sp_set_session_context @key = N'UserID', @value = @userId";
            command.Parameters.AddWithValue("@userId", userId);
            await command.ExecuteNonQueryAsync(cancellationToken);
        }
    }
}

步骤2:修改原有ADO.NET代码

替换connection.Open()为扩展方法,同时传入当前用户ID:

// 注入IHttpContextAccessor获取当前用户ID
private readonly IHttpContextAccessor _httpContextAccessor;

public YourService(IHttpContextAccessor httpContextAccessor)
{
    _httpContextAccessor = httpContextAccessor;
}

public void UpdateCustomer(Customer customer, string connectionString)
{
    var userId = _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);

    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        // 替换为扩展方法
        connection.OpenWithUserId(userId);

        string sql = "UPDATE Customer SET CompanyName = @CompanyName, EmailAddress = @EmailAddress WHERE CustomerID = @CustomerID";

        using (SqlCommand command = new SqlCommand(sql, connection))
        {
            command.Parameters.Clear();
            command.Parameters.AddWithValue("@CompanyName", customer.CompanyName);
            command.Parameters.AddWithValue("@EmailAddress", customer.EmailAddress);
            command.Parameters.AddWithValue("@CustomerID", customer.CustomerID);

            command.ExecuteNonQuery();
        }
    }
}

方案3:自定义SqlConnection类(高级统一拦截)

如果需要全局拦截所有原生SqlConnection的打开操作,可以继承SqlConnection并重写Open方法:

using System.Data.SqlClient;
using System.Threading;
using System.Threading.Tasks;

public class UserTrackingSqlConnection : SqlConnection
{
    private readonly string _userId;

    public UserTrackingSqlConnection(string connectionString, string userId) : base(connectionString)
    {
        _userId = userId;
    }

    public override void Open()
    {
        base.Open();
        SetSessionContext();
    }

    public override async Task OpenAsync(CancellationToken cancellationToken)
    {
        await base.OpenAsync(cancellationToken);
        await SetSessionContextAsync(cancellationToken);
    }

    private void SetSessionContext()
    {
        if (!string.IsNullOrEmpty(_userId))
        {
            using var command = CreateCommand();
            command.CommandText = "EXEC sp_set_session_context @key = N'UserID', @value = @userId";
            command.Parameters.AddWithValue("@userId", _userId);
            command.ExecuteNonQuery();
        }
    }

    private async Task SetSessionContextAsync(CancellationToken cancellationToken)
    {
        if (!string.IsNullOrEmpty(_userId))
        {
            using var command = CreateCommand();
            command.CommandText = "EXEC sp_set_session_context @key = N'UserID', @value = @userId";
            command.Parameters.AddWithValue("@userId", _userId);
            await command.ExecuteNonQueryAsync(cancellationToken);
        }
    }
}

使用时替换原SqlConnection:

var userId = _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);

using (SqlConnection connection = new UserTrackingSqlConnection(connectionString, userId))
{
    connection.Open();
    // 后续代码保持不变
}

内容的提问来源于stack exchange,提问作者Milacay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 13:52:04