EF6中使用Load()显式加载筛选后导航属性无效,上下文释放后访问报错求助
兄弟,你这个坑我之前也踩过!明明按EF6的文档用了显式加载的Load()方法,结果上下文一释放,访问导航属性就弹出这个闹心的错误,简直头大:
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
其实问题出在Load()的本质上——它虽然把筛选后的导航数据加载到内存里了,但这个导航属性对应的集合还是EF的代理对象(比如EntityCollection<T>),这个对象底层还是和上下文绑定着的,一旦上下文被释放,它就没法正常提供数据了,哪怕你已经加载过数据也没用。
给你几个亲测有效的解决办法:
最推荐:用投影直接一次性获取数据
别绕显式加载的弯子,直接用LINQ投影把实体和筛选后的导航属性数据一起查出来,这样拿到的是独立的普通集合,完全不依赖上下文,释放后也能随便访问。举个例子:using (var context = new YourDbContext()) { var targetData = context.YourMainEntities .Where(e => e.Id == targetId) .Select(e => new { MainEntity = e, FilteredSubItems = e.SubItems.Where(s => s.IsActive == true).ToList() }) .FirstOrDefault(); // 这里你可以把数据转成自己需要的实体类,或者直接用这个匿名对象里的内容 }如果必须用原实体类:固化导航属性集合
要是你一定要返回原始的实体类型,那可以先关闭EF的延迟加载和代理创建,然后显式加载后把导航属性转成List<T>,把数据“脱钩”上下文:using (var context = new YourDbContext()) { // 先关了延迟加载和代理,避免自动触发依赖上下文的操作 context.Configuration.LazyLoadingEnabled = false; context.Configuration.ProxyCreationEnabled = false; var mainEntity = context.YourMainEntities.Find(targetId); // 显式加载筛选后的导航属性 context.Entry(mainEntity) .Collection(e => e.SubItems) .Query() .Where(s => s.IsActive == true) .Load(); // 把导航属性转成List,断开和上下文的关联 mainEntity.SubItems = mainEntity.SubItems.ToList(); return mainEntity; }备选:用AsNoTracking()
给主实体查询加上AsNoTracking(),让EF不跟踪这个实体,不过这种方式下显式加载的写法要注意,还是不如投影来得直接稳妥,所以只作为备选:using (var context = new YourDbContext()) { var mainEntity = context.YourMainEntities.AsNoTracking() .FirstOrDefault(e => e.Id == targetId); // 这里显式加载的话,需要确保查询是独立的,或者还是建议用投影 }
总结一下,核心就是要让你拿到的导航属性数据是不依赖EF上下文的普通集合,别让它和上下文的代理对象绑定着,这样上下文释放后就不会报错啦。
备注:内容来源于stack exchange,提问作者SyndRain

