【问题标题】:Entity Framework: Why does the database get hit if the data is in the context?实体框架:如果数据在上下文中,为什么数据库会受到影响?
【发布时间】:2010-10-07 07:31:42
【问题描述】:

为什么要在数据库中找到已在 ObjectContext 中表示的记录?

所以这就是我认为当您查询时会发生的情况:

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

垃圾示例,但我的想法是我想从 id 为 1 的表中获取用户。现在假设这是第一次通过,所以我猜它必须在上下文中创建 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 列表中(如果我使用 Watch 挖掘得足够远)我可以看到上下文 SiteUser 列表中有一个项目,它恰好是代表此数据行的水合对象。

接下来我想更改一些内容而不保存:

someUser.UserName = "HIHIHI";

现在说出于某种原因我想再次抓取它使用相同的上下文(这是一个奇怪的例子,但它实际上是一个测试,所以我可以证明这种情况发生):

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

我认为会发生的是它会查看上下文中的 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];

我更改了数据库中的一个值(密码列)并让它进入数据库。对象没有任何变化。

这里似乎有些不对劲。它会访问数据库以选择我已经在上下文中水合的对象,但它不会应用我在数据库中手动进行的更改。那它为什么还要访问数据库呢?

更新 感谢下面的一些链接,我能够挖掘一下并找到这个:

Merge Option

看起来有一个枚举设置来告诉如何处理负载。现在读完之后,我看到了 MergeOption.AppendOnly:

已经存在的对象 对象上下文不是从 数据源。这是默认设置 查询或调用时的行为 上的 Load 方法 EntityCollection)>).

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

【问题讨论】:

    标签: entity-framework


    【解决方案1】:

    context.SiteUser 是 ObjectQuery 类型的属性。当您执行 ObjectQuery 时,它会总是访问后备存储。他们就是这样做的。如果您不想执行数据库查询,请不要使用 ObjectQuery。

    听起来你真正想要的是一个函数,它说:“如果实体已经在上下文中具体化,则返回它。如果不是,则从数据库中获取它。”碰巧,ObjectContext 包含这样一个函数,称为 GetObjectByKey

    GetObjectByKey 尝试检索一个 具有指定的对象 来自 ObjectStateManager 的 EntityKey。 如果对象当前未加载 进入对象上下文,查询是 执行以试图返回 来自数据源的对象。

    【讨论】:

    • Hrm 似乎很奇怪,默认情况下它不会这样做。我猜想默认的 MergeOption 说不查看数据库的更改,但仅认为在覆盖的情况下很烦人。再一次,它被称为 MergeOption...
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      如果您避免使用 .ToList() 并使用 .FirstOrDefault() 会怎样?

      【讨论】:

      • 不。每次运行 linq“查询”时,第一个(顶部)仍然会命中它。
      • 你做了 context.SiteUser.FirstOrDefault(role => role.UserID == 1) 吗?我想知道如果您检索到 otherUser 并将 someUser 中的数据与 otherUser 进行比较会发生什么?
      • 值仍然相同,即使您在数据库中以某种方式在水合 someUser 和 otherUser 之间更改它们。我怀疑这是因为它知道它在上下文中,并且默认情况下 MergeOption 说忽略更改。
      • 如果我没记错的话,你会得到相同的参考(因为你在相同的上下文中)。
      • 希望。如果没有,实体框架可能有一些可怕的错误。
      【解决方案4】:

      如果我理解你的问题,这应该回答它:

      其实实体框架的方式 默认情况下这样做是要求 从变更通知 实体对象到框架类 调用状态管理器,然后 跟踪哪些属性具有 被改变了。原始值为 仅按需复制。什么时候更新 发生,使用那些原始值 只有在与服务器交谈时 更改的属性标记为 “并发令牌”。也就是说,对于 任何并发令牌列,当 框架正在创建更新 声明它将包括一张支票 验证数据库中的行 仍然具有原始值,如果 不是它会引发异常 通知程序其他人 已更改数据库中的行。 实体也是如此 框架并不绝对需要 来自物业的通知 二传手,你也可以确定什么是 在应用程序代码中修改和 调用显式方法 框架来指示哪些属性 被改变(但随后框架 只会有一个记录 属性被修改,它不会有 原始值)。

      来自here。更多信息可以阅读herehere

      编辑添加:

      似乎在使用 EF 时,有一个 ObjectStateManager 可以跟踪从未真正允许断开数据的更改。为了断开数据,您必须调用 ObjectContext.Detach 方法来断开您的对象。更多内容请关注herehere

      【讨论】:

      • 唯一看起来不对的是这不涉及更新。现在我可以非常厚实(非常可能),或者我可以假设它默认情况下总是检查数据库,而不管选择、更新或其他什么。这是我应该假设的吗?
      • 这些链接将我引向一些我已经更新了我的原始帖子的内容,但不幸的是,这只会增加我对正在发生的事情的困惑。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 2021-05-19
      • 1970-01-01
      • 1970-01-01
      • 2020-05-24
      相关资源
      最近更新 更多