如何修复“ObjectContext 实例已被处置”
Posted
技术标签:
【中文标题】如何修复“ObjectContext 实例已被处置”【英文标题】:How can I fix "The ObjectContext instance has been disposed " 【发布时间】:2020-06-10 01:12:53 【问题描述】:我开发了一个带有实体框架的应用程序。我明白了
ObjectContext 实例已被释放,不能再使用 用于需要连接的操作。
有时会出错。
我在互联网上做了一些研究,但我无法弄清楚。如果您能提供帮助,我将不胜感激
家庭控制器:
public ActionResult Index()
return View(noteManager.ListQueryable().Where(x => x.IsDraft == false && x.IsApproved == true).OrderByDescending(x => x.ModifiedOn).Take(10).ToList());
我的笔记实体:
public class Note : MyEntitesBase
public string Tittle get; set;
public string Text get; set;
public bool IsDraft get; set;
public int LikeCount get; set;
public int CategoryID get; set;
public virtual EvernoteUser Owner get; set;
public virtual List<Comment> Comments get; set;
public virtual Category Category get; set;
public virtual List<Liked> Likes get; set;
public Note()
Comments = new List<Comment>();
Likes = new List<Liked>();
我的评论实体:
public class Comment : MyEntitesBase
public string Text get; set;
public bool CommentStatus get; set;
public virtual Note Note get; set;
public virtual EvernoteUser Owner get; set;
我的数据库上下文:
public class DatabaseContext :DbContext
public DbSet<EvernoteUser> EvernoteUsers get; set;
public DbSet<Note> Notes get; set;
public DbSet<Comment> Comments get; set;
public DbSet<Category> Categories get; set;
public DbSet<Liked> Likes get; set;
public DatabaseContext()
Database.SetInitializer(new MigrateDatabaseToLatestVersion<DatabaseContext,Configuration>());
【问题讨论】:
如何初始化noteManager
?而noteManager
和DBContext
是什么关系?
【参考方案1】:
这个问题通常源于序列化程序,它会触及被序列化实体上的所有属性以发送到视图。如果与实体关联的 DbContext 被释放,则序列化程序将在尝试发出查询以加载相关详细信息时遇到此问题。
如果noteManager.ListQueryable()
返回IQueryable<Note>
,快速解决方法是:
return View(noteManager.ListQueryable()
.Include(x => x.Owner)
.Include(x => x.Comments)
.Include(x => x.Category)
.Include(x => x.Likes)
.Where(x => x.IsDraft == false && x.IsApproved == true)
.OrderByDescending(x => x.ModifiedOn)
.Take(10).ToList());
这种渴望加载相关实体以及注释。急切加载和延迟加载之间的区别在于,对于急切加载,EF 将生成 SQL 以连接所有关联的表,然后检索最多 10 个选定行的相关行。使用延迟加载,您可能有 10 个注释行,例如 ID 为 1-10,但随着每个属性被触摸,EF 将生成如下查询:
SELECT * FROM Owners WHERE OwnerID = 22
-- 注释 1 上的所有者 ID
SELECT * FROM Comments WHERE NoteId = 1
SELECT * FROM Categories WHERE CategoryId = 4
注释 1 上的类别 ID
SELECT * FROM Likes WHERE NoteId = 1
然后再重复 9 次,每返回一个音符行一次。这是 EF 和 DB 在实体的代理持有对 DbContext 的弱引用时需要协商的大量查询。如果请求在序列化程序完成实体之前处理 DbContext,则您将收到一个正在处理的异常。
但是,即使使用即时加载,如果任何相关实体本身都有子实体,这也可能是一个兔子洞。加载视图可能不需要的所有相关数据也会对性能/资源产生影响。
更好的长期解决方案是定义可序列化的 ViewModel 来表示您的视图实际需要显示的数据结构,然后利用 Select
或 Automapper 的 ProjectTo
用实体结构中的数据填充该视图模型。这消除了急切加载数据的需要,只需来自结构的Select
,EF 就会计算出 SQL。它还消除了序列化程序中延迟加载命中的风险,前提是您来自实体而不是实体本身的 Select
字段。这也可以大大减少服务器和客户端在请求时存储数据所需的内存量以及数据传输大小。
将视图模型传递给视图意味着将相同或不同的视图模型传回服务器,而不是尝试传回实体、附加和保存...与加载再次数据并复制值。但是,这样做更安全,因为您不会冒陈旧、不完整或可能被篡改的数据覆盖真实数据的风险。在执行更新时,您应该始终重新加载实体,以验证并检查行在发送到客户端后是否未被修改。不信任来自 Web 客户端的任何内容。将字段复制到新加载的实体也意味着更有效的 UPDATE
语句,因为 Attaching + EntityState.Modified 或使用 DbContext.Update()
导致更新语句更新 all 字段,而不是复制,只有值被更改的将被添加到 UPDATE 语句中。
【讨论】:
以上是关于如何修复“ObjectContext 实例已被处置”的主要内容,如果未能解决你的问题,请参考以下文章