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

.NET 6中实现DataTable服务端过滤及获取HttpContext的方案咨询

.NET 6 迁移中 HttpContext 为空问题的解决方法

问题根源

  1. API 体系差异:.NET Framework 的 System.Web.HttpContext 和 .NET 6 的 Microsoft.AspNetCore.Http.HttpContext 是完全独立的实现,无法直接兼容。
  2. 静态方法设计缺陷:原静态 FilterTable 方法直接依赖请求上下文,而静态方法是全局无状态的,HttpContext 是请求级别的资源,在异步操作、后台线程等场景下极易出现为空的情况。
  3. 注入时机问题:即便注册了 IHttpContextAccessor,如果在请求管道外调用静态方法,也会获取不到有效上下文。

最优解决方案:解耦上下文依赖

直接将方法需要的 bSearchable_* 参数从 HttpContext 中提取后传入方法,彻底消除对请求上下文的依赖,同时避免线程安全问题。

步骤1:控制器中提取请求参数

在调用过滤方法前,先从请求中收集所有 bSearchable 标记:

[HttpPost]
public IActionResult GetFilteredData(JQPM p)
{
    // 假设从数据源获取原始DataTable
    DataTable rawDt = GetRawDataTable();
    int totalCount = 0;

    // 收集前端传入的bSearchable参数列表
    var searchableFlags = new List<bool>();
    var columnCount = p.sColumns.Split(',').Length;
    for (int j = 0; j < columnCount; j++)
    {
        bool isSearchable = bool.TryParse(Request.Form["bSearchable_" + j], out var result) ? result : false;
        searchableFlags.Add(isSearchable);
    }

    // 调用重构后的过滤方法
    var filteredDt = FilterTable(rawDt, ref totalCount, p, searchableFlags);

    return Json(new { data = filteredDt, recordsTotal = totalCount });
}

步骤2:重构 FilterTable 方法

移除静态修饰(改为实例方法或保留静态但传入参数),不再直接读取 HttpContext:

public DataTable FilterTable(DataTable dt, ref int totalCount, JQPM p, List<bool> searchableFlags)
{
    p.sColumnslist = p.sColumns.Split(',');
    totalCount = dt.Rows.Count;

    if (!string.IsNullOrEmpty(p.sSearch))
    {
        // 拆分搜索关键词并过滤空值
        var searchTerms = p.sSearch.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries);
        foreach (var term in searchTerms)
        {
            // 用Linq替代字符串拼接查询,避免SQL注入
            var filteredRows = dt.AsEnumerable().Where(row => 
            {
                for (int j = 0; j < p.sColumnslist.Length; j++)
                {
                    if (!searchableFlags[j]) continue;

                    var column = dt.Columns[p.sColumnslist[j]];
                    var cellValue = row[column];
                    if (cellValue == DBNull.Value) continue;

                    // 根据列类型匹配关键词
                    if (column.DataType == typeof(string))
                    {
                        if (((string)cellValue).Contains(term, StringComparison.OrdinalIgnoreCase))
                            return true;
                    }
                    else if (column.DataType == typeof(DateTime))
                    {
                        if (((DateTime)cellValue).ToString("yyyy-MM-dd HH:mm:ss").Contains(term))
                            return true;
                    }
                    else if (column.DataType == typeof(int))
                    {
                        if (int.TryParse(term, out var num) && (int)cellValue == num)
                            return true;
                    }
                }
                return false;
            });

            dt = filteredRows.CopyToDataTable();
            totalCount = dt.Rows.Count;
        }
    }

    // 执行分页逻辑(对应原GetDatatable方法)
    dt = ApplyPagination(dt, ref totalCount, p);
    return dt;
}

// 分页逻辑示例
private DataTable ApplyPagination(DataTable dt, ref int totalCount, JQPM p)
{
    var startIndex = p.iDisplayStart;
    var pageSize = p.iDisplayLength;
    if (startIndex + pageSize > dt.Rows.Count)
        pageSize = dt.Rows.Count - startIndex;

    var paginatedDt = dt.Clone();
    for (int i = startIndex; i < startIndex + pageSize; i++)
    {
        paginatedDt.ImportRow(dt.Rows[i]);
    }
    return paginatedDt;
}

备选方案:正确使用 IHttpContextAccessor

如果必须保留上下文依赖,需确保注入时机和调用场景正确:

1. 注册 HttpContextAccessor

在 Program.cs 中添加服务注册:

builder.Services.AddHttpContextAccessor();

2. 创建过滤服务类

将方法改为实例方法,通过构造注入获取 IHttpContextAccessor:

public class TableFilterService
{
    private readonly IHttpContextAccessor _httpContextAccessor;

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

    public DataTable FilterTable(DataTable dt, ref int totalCount, JQPM p)
    {
        var request = _httpContextAccessor.HttpContext?.Request;
        if (request == null)
            throw new InvalidOperationException("当前无有效请求上下文");

        p.sColumnslist = p.sColumns.Split(',');
        totalCount = dt.Rows.Count;

        if (!string.IsNullOrEmpty(p.sSearch))
        {
            var searchTerms = p.sSearch.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries);
            foreach (var term in searchTerms)
            {
                string filterExpr = "0=1 ";
                for (int j = 0; j < p.sColumnslist.Length; j++)
                {
                    bool isSearchable = bool.TryParse(request.Form["bSearchable_" + j], out var result) ? result : false;
                    if (!isSearchable) continue;

                    var column = dt.Columns[p.sColumnslist[j]];
                    if (column.DataType == typeof(string))
                    {
                        filterExpr += $" or [{column.ColumnName}] LIKE '%{term}%'";
                    }
                    else if (column.DataType == typeof(DateTime))
                    {
                        filterExpr += $" or CONVERT(varchar, [{column.ColumnName}], 120) LIKE '%{term}%'";
                    }
                    else if (column.DataType == typeof(int) && int.TryParse(term, out var num))
                    {
                        filterExpr += $" or [{column.ColumnName}] = {num}";
                    }
                }

                dt = dt.Select(filterExpr).CopyToDataTable();
                totalCount = dt.Rows.Count;
            }
        }

        dt = ApplyPagination(dt, ref totalCount, p);
        return dt;
    }
}

3. 注册并使用服务

在 Program.cs 注册服务:

builder.Services.AddScoped<TableFilterService>();

在控制器中注入使用:

public class DataController : ControllerBase
{
    private readonly TableFilterService _filterService;

    public DataController(TableFilterService filterService)
    {
        _filterService = filterService;
    }

    [HttpPost]
    public IActionResult GetFilteredData(JQPM p)
    {
        DataTable rawDt = GetRawDataTable();
        int totalCount = 0;
        var filteredDt = _filterService.FilterTable(rawDt, ref totalCount, p);
        return Json(new { data = filteredDt, recordsTotal = totalCount });
    }
}

关键注意事项

  • 避免SQL注入:原代码直接拼接字符串到查询表达式中存在严重安全风险,建议用Linq to DataTable替代。
  • 静态方法慎用:静态方法不应依赖请求上下文这类请求级资源,否则会引发线程安全问题和上下文为空异常。
  • 上下文有效性:使用 IHttpContextAccessor 时,必须确保在请求管道内调用方法(避免在后台任务、异步回调中使用)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 16:18:21