无法通过Ajax返回含多列表的单类对象,遇ObjectContext已释放错误
解决"The ObjectContext instance has been disposed"错误及Ajax数据获取问题
问题背景
我在开发过程中遇到了一个头疼的错误,视图里弹出了这个提示:
The ObjectContext instance has been disposed. ....
我的视图模型类是这样定义的:
public class TruckLUOrderVD { public raTMS_TruckLoadingOrder TruckLoadingOrder { get; set; } public List<raTMS_CompartmentLoadingOrder> TruckCompartment { get; set; } public List<raTMS_Tank> TankTruck { get; set; } public List<SelectListItem> LTank { get; set; } public int LJetty { get; set; } public List<raTMS_ShipAgent> Agent { get; set; } public List<raTMS_Ship> Ship { get; set; } public List<string> service = new List<string> { "Loading", "Unloading"}; public List<SelectListItem> modeList { get; set; } public List<Boolean> compartmentEnabled { get; set; } public string shiptype { get; set; } public Boolean multiolder { get; set; } public string maxcmp { get; set; } public string serviceval { get; set; } public string statusname { get; set; } public string serviceAutoNumber { get; set; } public string[] productnm { get; set; } }
我通过Ajax调用下面这个控制器Action获取数据:
public JsonResult Getdetail(int id) { raTMS_Tank TANK = new raTMS_Tank(); raTMS_Ship Ship = new raTMS_Ship(); raTMS_ShipAgent agent = new raTMS_ShipAgent(); raTMS_ShipOrder order = new raTMS_ShipOrder(); raTMS_ShipTankUnloadingOrder stank = new raTMS_ShipTankUnloadingOrder(); raTMS_TruckLoadingOrder SR = new raTMS_TruckLoadingOrder(); List<raTMS_TruckLoadingOrder> ListA = new List<raTMS_TruckLoadingOrder>(); List<raTMS_CompartmentLoadingOrder> ListB = new List<raTMS_CompartmentLoadingOrder>(); List<raTMS_ShipTankUnloadingOrder> ListC = new List<raTMS_ShipTankUnloadingOrder>(); TruckLUOrderVD vd = new TruckLUOrderVD(); using (raTMSEntities2 db = new raTMSEntities2()) { var c = db.raTMS_CompartmentLoadingOrder.Where(x => x.TruckLoadingOrderFK == id).ToList(); SR = db.raTMS_TruckLoadingOrder.Where(x => x.PriKey == id).FirstOrDefault(); for (int i = 0; i < SR.raTMS_Truck.CompartmentNumber; i++) { var c1 = c.Where(x => x.CompartmentNumber == i + 1).FirstOrDefault(); if (c1 != null) { ListB.Add(c1); ListB[i].OrderPrikey = c1.OrderFK; ListB[i].FQTransact = c1.raTMS_Order.TansactQty.GetValueOrDefault(); ListB[i].AQReserved = c1.raTMS_Order.ReservedQty.GetValueOrDefault(); } else { ListB.Add(new raTMS_CompartmentLoadingOrder()); } } raTMS_Status stat = new raTMS_Status(); vd.statusname = stat.getStatusName(SR.Status.Value); vd.TruckLoadingOrder = SR; vd.TruckCompartment = ListB; return Json(new { vd = vd }, JsonRequestBehavior.AllowGet); } }
我已经试过关闭EF的延迟加载和代理创建:
db.Configuration.LazyLoadingEnabled = false; db.Configuration.ProxyCreationEnabled = false;
现在的情况是,直接返回视图模型没问题,但Ajax就是拿不到数据,我需要做无刷新操作,想问问我的Linq查询是不是有问题?
问题分析与解决方案
咱们先把核心问题揪出来:你关闭了延迟加载,但代码里还是存在跨上下文访问导航属性的情况,而且直接把EF实体传给前端序列化,这才是导致ObjectContext disposed错误的根源,和Linq查询逻辑本身关系不大。
1. 为什么会出错?
- 虽然你在
using块里关闭了延迟加载,但EF实体本身可能还保留着导航属性的引用。当JSON序列化器在using块外(因为返回Json后上下文已经释放)尝试遍历vd对象的所有属性时,会去访问那些没加载的导航属性,比如SR.raTMS_Truck或者c1.raTMS_Order,这时候上下文已经被释放,就会抛出错误。 - 另外,你在循环里直接把从上下文拿到的
c1添加到ListB,后续访问c1.raTMS_Order时,如果没预先加载这个导航属性,即使关闭了延迟加载,也可能拿到null,或者序列化时还是会触发对上下文的访问。
2. 具体修复步骤
第一步:显式加载所有需要的导航属性
用Include方法在查询时一次性加载所有要用到的关联数据,彻底摆脱对延迟加载的依赖:
// 查询TruckLoadingOrder时,同时加载关联的raTMS_Truck SR = db.raTMS_TruckLoadingOrder .Include(x => x.raTMS_Truck) .Where(x => x.PriKey == id) .FirstOrDefault(); // 查询CompartmentLoadingOrder时,同时加载关联的raTMS_Order var c = db.raTMS_CompartmentLoadingOrder .Include(x => x.raTMS_Order) .Where(x => x.TruckLoadingOrderFK == id) .ToList();
第二步:用DTO替代EF实体传递给前端
EF实体带有很多上下文相关的元数据,直接序列化很容易踩坑。最好的做法是创建数据传输对象(DTO),只把前端需要的属性复制进去:
比如,给raTMS_TruckLoadingOrder创建对应的DTO:
public class TruckLoadingOrderDTO { // 只保留前端需要的属性,按需添加 public int PriKey { get; set; } public int? Status { get; set; } // 其他需要展示/使用的属性... }
同样给raTMS_CompartmentLoadingOrder创建DTO:
public class CompartmentLoadingOrderDTO { public int OrderPrikey { get; set; } public decimal FQTransact { get; set; } public decimal AQReserved { get; set; } // 其他需要的属性... }
然后在Action里把EF实体的属性映射到DTO:
// 映射TruckLoadingOrder到DTO vd.TruckLoadingOrder = new TruckLoadingOrderDTO { PriKey = SR.PriKey, Status = SR.Status, // 其他属性赋值... }; // 映射CompartmentLoadingOrder到DTO if (c1 != null) { compartmentDTOs.Add(new CompartmentLoadingOrderDTO { OrderPrikey = c1.OrderFK, FQTransact = c1.raTMS_Order.TansactQty.GetValueOrDefault(), AQReserved = c1.raTMS_Order.ReservedQty.GetValueOrDefault(), }); }
第三步:优化JSON序列化配置
可以让序列化器忽略null值,避免因为未加载的属性导致的问题:
return Json(new { vd = vd }, JsonRequestBehavior.AllowGet, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
3. 调整后的Action简化版
public JsonResult Getdetail(int id) { TruckLUOrderVD vd = new TruckLUOrderVD(); using (raTMSEntities2 db = new raTMSEntities2()) { // 关闭延迟加载和代理创建 db.Configuration.LazyLoadingEnabled = false; db.Configuration.ProxyCreationEnabled = false; // 显式加载关联数据 var compartmentOrders = db.raTMS_CompartmentLoadingOrder .Include(x => x.raTMS_Order) .Where(x => x.TruckLoadingOrderFK == id) .ToList(); var truckLoadingOrder = db.raTMS_TruckLoadingOrder .Include(x => x.raTMS_Truck) .Where(x => x.PriKey == id) .FirstOrDefault(); List<CompartmentLoadingOrderDTO> compartmentDTOs = new List<CompartmentLoadingOrderDTO>(); if (truckLoadingOrder != null && truckLoadingOrder.raTMS_Truck != null) { for (int i = 0; i < truckLoadingOrder.raTMS_Truck.CompartmentNumber; i++) { var c1 = compartmentOrders.Where(x => x.CompartmentNumber == i + 1).FirstOrDefault(); if (c1 != null) { compartmentDTOs.Add(new CompartmentLoadingOrderDTO { OrderPrikey = c1.OrderFK, FQTransact = c1.raTMS_Order.TansactQty.GetValueOrDefault(), AQReserved = c1.raTMS_Order.ReservedQty.GetValueOrDefault(), }); } else { compartmentDTOs.Add(new CompartmentLoadingOrderDTO()); } } raTMS_Status stat = new raTMS_Status(); vd.statusname = stat.getStatusName(truckLoadingOrder.Status.Value); vd.TruckLoadingOrder = new TruckLoadingOrderDTO { PriKey = truckLoadingOrder.PriKey, Status = truckLoadingOrder.Status, }; vd.TruckCompartment = compartmentDTOs; } return Json(new { vd = vd }, JsonRequestBehavior.AllowGet, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); } }
总结
你的Linq查询逻辑本身是没问题的,问题出在没有显式加载导航属性以及直接传递EF实体给前端。通过显式加载关联数据、用DTO隔离EF实体,就能解决序列化时访问已释放上下文的问题,让Ajax请求正常返回数据。
内容的提问来源于stack exchange,提问作者MEgiver
相关产品推荐
相关产品推荐

