.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
相关产品推荐
相关产品推荐

