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

无法通过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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:41:44