我开发了一个带有实体框架的应用程序。我得到
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>());
}
}
此问题通常源于序列化程序,它将触及要序列化的实体上的所有属性以发送到视图。如果实体关联的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
注释1 上的SELECT * FROM Categories WHERE CategoryId = 4
类别ID
SELECT * FROM Likes WHERE NoteId = 1
然后再重复9次,每个返回的音符行重复一次。当实体的代理持有对DbContext的弱引用时,EF和DB需要协商大量查询。如果请求在序列化程序完成实体之前处理了DbContext,则会收到一个正在处理的Exception。
然而,即使在急切加载的情况下,如果这些相关实体中的任何一个本身都有子实体,这也可能是一个兔子洞。加载视图可能不需要的所有相关数据也会带来性能/资源方面的影响。
更好的长期解决方案是定义可序列化的ViewModels来表示视图实际需要显示的数据结构,然后利用Select
或Automapper的ProjectTo
用实体结构中的数据填充视图模型。这就消除了急于加载数据的需要,只需从结构中选择Select
,EF就会计算出SQL。它还消除了序列化程序中延迟加载命中的风险,为您提供了来自实体而非实体本身的Select
字段。这也可以大大减少服务器上所需的内存量&客户端在请求时存储数据以及数据传输大小。
将视图模型传递给视图意味着将相同或不同的视图模型传递回服务器,而不是试图传递回实体、附加和保存。。。与再次加载数据和跨数据复制值相比,这看起来需要更多的工作和时间。然而,这要安全得多,因为你不会冒着过时、不完整或可能被篡改的数据覆盖真实数据的风险。在执行更新时,您应该始终重新加载实体,以验证和检查行自发送到客户端以来是否未被修改。不要相信来自网络客户端的任何东西。将字段复制到新加载的实体也意味着更高效的UPDATE
语句作为Attaching+EntityState。修改或使用DbContext.Update()
会导致更新所有字段的更新语句,而使用跨复制时,只有更改的值才会添加到update语句中。