Entity framework 实体框架:如果数据在上下文中,为什么数据库会被击中?

Entity framework 实体框架:如果数据在上下文中,为什么数据库会被击中?,entity-framework,Entity Framework,为什么要点击数据库来查找已经在ObjectContext中表示的记录 因此,当你询问时,我认为会发生以下情况: SiteUser someUser=context.SiteUser.Where(role=>role.UserID==1.ToList()[0]; 例如,我想从表中获取一个id为1的用户。现在假设这是第一次,我猜它必须在上下文中创建SiteUser列表,查询数据库,然后填充它的列表。使用profiler,我看到了以下内容: SELECT [Extent1].[UserID]

为什么要点击数据库来查找已经在ObjectContext中表示的记录

因此,当你询问时,我认为会发生以下情况:


SiteUser someUser=context.SiteUser.Where(role=>role.UserID==1.ToList()[0];

例如,我想从表中获取一个id为1的用户。现在假设这是第一次,我猜它必须在上下文中创建SiteUser列表,查询数据库,然后填充它的列表。使用profiler,我看到了以下内容:

SELECT 
 [Extent1].[UserID] AS [UserID], 
 [Extent1].[MainEmail] AS [MainEmail], 
 [Extent1].[Password] AS [Password], 
 [Extent1].[UserName] AS [UserName]
FROM [TIDBA].[TI_USER] AS [Extent1]
WHERE 1 = [Extent1].[UserID]
漂亮。它做到了我所期望的,并且在SiteUser列表中(如果我使用Watch深入挖掘),我可以看到上下文SiteUser列表中有一个项目,它恰好是表示此数据行的水合对象

接下来,我想在不保存的情况下更改某些内容:

someUser.UserName = "HIHIHI";
现在,出于某种原因,我想使用相同的上下文再次抓取它(这是一个奇怪的示例,但实际上是一个测试,因此我可以证明这一点):

我认为它会在上下文中查看SiteUser列表,因为生成的属性就是这么说的。(如果不是空的,返回列表)然后它会查看它是否存在并返回它。没有数据库命中。猜猜档案员怎么说

SELECT 
 [Extent1].[UserID] AS [UserID], 
 [Extent1].[MainEmail] AS [MainEmail], 
 [Extent1].[Password] AS [Password], 
 [Extent1].[UserName] AS [UserName]
FROM [TIDBA].[TI_USER] AS [Extent1]
WHERE 1 = [Extent1].[UserID]
人力资源管理。好吧,我开始想,也许这是一个彻底的检查,看看这个数据项上是否有任何变化,并且只在客户端上我没有改变的值上更新SiteUser对象。(有点像context.Refresh(RefreshMode.ClientWins,context.SiteUser)),所以我把它停在:

someUser = context.SiteUser.Where(role => role.UserID == 1).ToList()[0];
行和我更改数据库中的一个值(密码列),并让它访问数据库。物体上没有任何变化

这里好像有点不对劲。它点击数据库来选择我在上下文中已经水合的对象,但它没有应用我在数据库中手动进行的更改。那么,它为什么会进入数据库呢

更新 多亏了下面的一些链接,我才能够深入挖掘并找到以下内容:

看起来有一个枚举被设置为告诉如何处理负载。现在在阅读之后,我看到了MergeOption.AppendOnly的以下内容:

中已存在的对象 对象上下文未从中加载 数据源。这是默认设置 查询或调用时的行为 平面上的荷载法 实体收集)


这意味着,如果我在上下文中有它,就不会对数据库造成影响。然而,这似乎不是真的。如果OverwriteChanges或PreserveChanges是默认值,这是有意义的,但它们不是。这似乎与应该发生的事情相矛盾。我能想到的唯一一件事就是“加载”仅仅意味着没有覆盖。但是,这并不意味着没有对数据库的查询。

如果我理解您的问题,这应该可以回答:

实际上,实体框架 这在默认情况下是否需要 来自 框架类的实体对象 打电话给州经理 跟踪哪些属性具有 已经改变了。原始值为 仅按需复制。更新时 如果发生这种情况,将使用这些原始值 仅当 更改的属性标记为 “并发令牌”。就是 任何并发令牌列,当 框架正在创建更新 它将包括一张支票 验证数据库中的行 仍然具有原始值,如果 不,它将引发一个例外 通知程序其他人 已更改数据库中的行。 同样,实体 框架并不是绝对需要 来自属性的通知 setters,您还可以确定 在应用程序代码和 在上调用显式方法 框架来指示哪些属性 都发生了变化(但随后框架发生了变化) 只会有一个 属性已修改,它将没有 原值)

它来自于。可以阅读更多关于它的信息,以及

编辑以添加:


似乎有了EF,就有了一个ObjectStateManager,它可以跟踪从未真正允许断开连接的数据的更改。为了断开数据连接,必须调用ObjectContext.Detach方法断开对象连接。可以找到更多信息。

如果您避免使用.ToList()而使用.FirstOrDefault(),该怎么办?

IMO,EF第二次访问数据库的原因是确保数据库中没有满足查询的任何其他行。可能自发出第一个查询以来,已将其他相关行插入表中,EF正在查看是否存在这些行。

context.SiteUser是ObjectQuery类型的属性。当您执行ObjectQuery时,它将始终命中备份存储。他们就是这么做的。如果不希望执行数据库查询,则不要使用ObjectQuery

听起来你真正想要的是一个函数,它说,“如果实体已经在上下文中具体化了,那么就返回它。如果没有,那么就从数据库中获取它。”事实上,ObjectContext包含这样一个函数,称为

GetObjectByKey尝试检索 对象,该对象具有指定的 ObjectStateManager中的EntityKey。 如果对象当前未加载 在对象上下文中,查询是 在试图返回 对象从数据源中删除


唯一一件看起来不对劲的事情是,这不涉及更新。现在,我可以非常厚(很可能),也可以假设默认情况下它总是检查数据库,而不管选择、udpate或其他什么。这就是我应该假设的吗?这些链接让我找到了一些我已经更新过的原始帖子
someUser = context.SiteUser.Where(role => role.UserID == 1).ToList()[0];