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

.NET Core与Ajax:数据返回不全及加载缓慢问题排查

问题解决与优化方案

一、核心问题定位

1. 后端重复序列化引发数据异常

你的Controller代码执行了两次JSON序列化操作:

  • 第一次将maincategorylist序列化为字符串jsonData
  • 第二次又对该字符串再次序列化得到xc,最终返回的是经过双重转义的JSON字符串
    这会导致前端拿到的数据结构混乱,解析时容易出现部分数据无法正常识别的情况,直接造成仅4条数据渲染成功的问题。

同时,PreserveReferencesHandling.Objects配置如果没有循环引用场景(比如实体类间无互相引用),会额外生成$id等冗余字段,既增加JSON体积,也拖慢序列化和传输速度。

2. 前端解析逻辑错误+频繁DOM操作

前端针对双重序列化的数据做了错误解析:先执行jQuery.parseJSON(data),又用JSON.parse("[" + item + "]")强行包裹数组,这种操作破坏了原数据结构,导致部分数据无法被遍历到。另外,每次循环都调用$("#table-maincategories").append(),频繁操作DOM会引发页面重排,哪怕只有6条数据也会让加载显得卡顿。

二、修复步骤

1. 修复后端Controller代码

移除重复序列化,按需保留循环引用配置:

public async Task<JsonResult> MainCategories()
{
    var maincategorylist = await appDbContext.BusinessMainCategories
        .OrderBy(p => p.Name)
        .ToListAsync();

    // 直接返回实体列表,ASP.NET会自动处理序列化
    return Json(maincategorylist);
}

如果确实存在实体循环引用(比如BusinessMainCategories关联其他实体),改用更轻量化的配置:

public async Task<JsonResult> MainCategories()
{
    var maincategorylist = await appDbContext.BusinessMainCategories
        .OrderBy(p => p.Name)
        .ToListAsync();

    return Json(maincategorylist, new JsonSerializerSettings
    {
        // 忽略循环引用,避免冗余字段
        ReferenceLoopHandling = ReferenceLoopHandling.Ignore
    });
}

2. 修复前端Script代码

简化解析逻辑,改为一次性拼接DOM字符串再插入:

$("#mainCategoryList").click(function () {
    $.ajax({
        url: "/Admin/BusinessCategories/MainCategories/",
        contentType: "application/json",
        dataType: "json",
        type: "Get",
        success: function (data) {
            $("#table-maincategories").empty();
            let htmlStr = "";
            // 直接遍历后端返回的数组
            data.forEach(element => {
                htmlStr += `<tr class="text-center">
                    <td><img src="${element.Image}" style="height:80px;" /></td>
                    <td>${element.Name}</td>
                    <td>
                        <div class="form-check">
                            <a href="/Admin/BusinessCategories/MainShowMenu/${element.Id}" onclick="location.href=this.href;">
                                <input ${element.ShowMenu ? 'checked' : ''} class="form-check-input" type="checkbox" id="flex_${element.Id}">
                            </a>
                            <label class="form-check-label" for="flex_${element.Id}">Menüde Göster</label>
                        </div>
                    </td>
                    <td>
                        <a asp-action="EditMainCategory" asp-route-id="${element.Id}" class="btn btn-primary"><i class="bx bx-edit"></i> Düzenle</a>
                        <a asp-action="DeleteMainCategory" asp-route-id="${element.Id}" data-name="${element.Name}" class="btn btn-danger delete-button"><i class="bx bx-trash"></i> Sil</a>
                    </td>
                </tr>`;
            });
            // 一次性插入DOM,减少页面重排
            $("#table-maincategories").append(htmlStr);
        }
    })
});

注意:原代码中checked="${element.ShowMenu}"的写法存在逻辑错误——布尔值为false时会生成checked="false",这在HTML中仍会被视为选中状态,改用三元表达式判断更准确。

三、更优的数据获取方案

1. 投影查询按需取数

如果BusinessMainCategories表字段较多,但前端仅需Id、Name、Image、ShowMenu这几个字段,用投影查询只获取必要数据,减少传输量和序列化开销:

public async Task<JsonResult> MainCategories()
{
    var maincategorylist = await appDbContext.BusinessMainCategories
        .OrderBy(p => p.Name)
        .Select(p => new 
        {
            p.Id,
            p.Name,
            p.Image,
            p.ShowMenu
        })
        .ToListAsync();

    return Json(maincategorylist);
}

2. 添加内存缓存(可选)

如果分类数据不频繁更新,可添加内存缓存避免重复查询数据库:

private readonly IMemoryCache _memoryCache;
// 构造函数注入IMemoryCache
public YourController(AppDbContext appDbContext, IMemoryCache memoryCache)
{
    appDbContext = appDbContext;
    _memoryCache = memoryCache;
}

public async Task<JsonResult> MainCategories()
{
    var cacheKey = "MainCategoryList";
    if (!_memoryCache.TryGetValue(cacheKey, out List<object> maincategorylist))
    {
        maincategorylist = await appDbContext.BusinessMainCategories
            .OrderBy(p => p.Name)
            .Select(p => new 
            {
                p.Id,
                p.Name,
                p.Image,
                p.ShowMenu
            })
            .ToListAsync();

        // 设置缓存1小时过期
        var cacheOptions = new MemoryCacheEntryOptions()
            .SetAbsoluteExpiration(TimeSpan.FromHours(1));
        _memoryCache.Set(cacheKey, maincategorylist, cacheOptions);
    }

    return Json(maincategorylist);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 06:01:46