MVC返回Json的LINQ联表查询结果无法被Ajax success方法接收
我尝试将三张表关联的LINQ join查询结果通过控制器的Json Result返回给前端Ajax的success回调函数,但目前无法获取到任何返回值,success函数不会触发,只会执行error函数。该LINQ查询可以生成正确结果,在return Json(query)或return new JsonResult{ Data = query, JsonRequestBehavior = JsonRequestBehavior.AllowGet };处打断点时,query变量中已包含有效数据,问题在于查询结果无法从控制器成功传递到前端Ajax函数,我已尝试其他可行方案均未解决。
备注:我已创建专用的模型类存储联表查询结果。
控制器代码
[HttpPost] public JsonResult getscore(int val) { Debug.WriteLine("checking {0}", val); var g = val; List<Participant_info> p_details = context.Participant_info.ToList(); List<Participant_enrolled> e_details = context.Participant_enrolled.ToList(); List<Contest_marking> mark = context.Contest_marking.ToList(); var query = (from p in p_details join e in e_details on p.stud_id equals e.stud_id join m in mark on e.E_id equals m.E_id select new ScoreViewModel { partVm = p, enrollVm = e, markVm = m }).ToList(); foreach (var u in query) { Debug.WriteLine("name {0} mark{1} Contest_id {2} ", u.partVm.stud_name, u.markVm.C1st_marks, u.enrollVm.Contest_id); } return Json(query, JsonRequestBehavior.AllowGet); //return new JsonResult { Data = query, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; }
模型类代码
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace CEN_CONTEST_MGT_SYSTEM.Models { public class ScoreViewModel { public Participant_info partVm { get; set; } public Participant_enrolled enrollVm { get; set; } public Contest_marking markVm { get; set; } } }
视图层代码
@model IEnumerable<CEN_CONTEST_MGT_SYSTEM.Models.ScoreViewModel> @{ ViewBag.Title = "score"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h2>score</h2> @Html.DropDownList("Trial", "--- Tous ---") <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> <script type="text/javascript" src="http://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script> <script type="text/javascript"> $("#Trial").change(function () { val = $("#Trial").val(); alert("fff" + val); $.ajax({ type: "POST", url: "/judge_dashboard/getscore", data: JSON.stringify({'val': val}), contentType: "application/json; charset=utf-8", dataType: "json", async: true, processData: false, cache: false, success: function (data) { alert("hello"); }, error: function () { alert("error"); } }) }) </script>
90%以上概率是JSON序列化失败:你直接把EF实体对象放到ViewModel中返回,EF实体通常带有导航属性(比如Participant_info里可能包含关联的Participant_enrolled集合,Participant_enrolled又反过来关联Participant_info),序列化JSON时会触发循环引用,导致服务端抛出500异常,前端无法拿到合法JSON响应就会进入error回调。
方案1(最推荐):修改ViewModel,只返回前端需要的字段,不要直接存储EF实体对象
调整ScoreViewModel定义,仅保留需要使用的属性:public class ScoreViewModel { public string StudName { get; set; } public decimal C1stMarks { get; set; } public int ContestId { get; set; } // 其他前端需要用到的字段按需添加即可 }修改LINQ查询,直接为ViewModel属性赋值:
var query = (from p in p_details join e in e_details on p.stud_id equals e.stud_id join m in mark on e.E_id equals m.E_id select new ScoreViewModel { StudName = p.stud_name, C1stMarks = m.C1st_marks, ContestId = e.Contest_id // 其他字段逐一赋值 }).ToList();这个方案既可以避免循环引用问题,也能减少不必要的数据传输,性能和安全性更好。
方案2:先查看具体错误信息,定位问题
修改前端ajax的error回调,输出详细错误内容:error: function (xhr, status, error) { console.log("错误状态码:" + xhr.status); console.log("错误详情:" + error); console.log("服务端返回内容:" + xhr.responseText); alert("请求出错,请打开浏览器控制台查看详情"); }按F12打开浏览器开发者工具的控制台,就能看到具体错误类型,如果是500错误基本可以确认是序列化问题。
方案3(临时兼容方案):配置JSON序列化忽略循环引用
如果你一定要保留整个EF实体的结构,可以在项目的Global.asax.cs的Application_Start方法中添加配置:protected void Application_Start() { // 原有配置不变,新增下面一行 GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; }注意这个方案会返回很多冗余字段,不推荐长期使用。
小优化:你的接口标记了[HttpPost],不需要加JsonRequestBehavior.AllowGet参数,这个参数仅用于GET请求返回JSON的场景,POST请求可以直接写
return Json(query);。
内容的提问来源于stack exchange,提问作者Muneeba

