为什么要查找数据库以查找已在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列表,因为这就是生成的属性所说的内容。 (如果不为null,则返回列表)然后它会查看它是否存在并返回它。没有数据库命中。猜猜个人说什么......
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:
已经存在的对象 对象上下文未从中加载 数据源。这是默认值 查询或调用时的行为 上的Load方法 EntityCollection<(Of<(TEntity>)>)。
这表明如果我在上下文中有它,那么数据库就不会受到影响。但是,这似乎不是真的。如果OverwriteChanges或PreserveChanges是默认值,那将是有意义的,但它们不是。这似乎与应该发生的事情相矛盾。我唯一能想到的是“加载”只意味着没有覆盖。但是,这并不意味着数据库没有查询。
答案 0 :(得分:8)
context.SiteUser是ObjectQuery类型的属性。当您执行ObjectQuery时,它将始终命中后备存储。这就是他们所做的。如果您不想执行数据库查询,则不要使用ObjectQuery。
听起来你真正想要的是一个函数,它说:“如果实体已在上下文中实现,那么只需返回它。如果不是,那么就从数据库中获取它。”碰巧的是,ObjectContext包含了一个名为 GetObjectByKey
的函数GetObjectByKey尝试检索 具有指定的对象 来自ObjectStateManager的EntityKey。 如果当前未加载该对象 进入对象上下文,查询是 执行以试图返回 来自数据源的对象。
答案 1 :(得分:2)
IMO,EF第二次访问数据库的原因是确保数据库中没有任何满足查询的额外行。自从发出第一个查询以来,可能已将其他相关行插入到表中,并且EF会查看是否存在任何相关行。
答案 2 :(得分:1)
如果我理解你的问题,这应该回答:
实际上,实体框架的方式 这默认是要求 来自的变更通知 实体对象到框架类 然后称为州经理 跟踪哪些属性 被改变了。原始值是 仅按需复制。何时更新 发生时,使用那些原始值 只有在与服务器通信时 已更改的属性标记为 “并发令牌”。也就是说 任何并发令牌列,何时 该框架正在创建一个更新 声明它将包括一张支票 验证数据库中的行 仍然具有原始价值,如果 不会引起例外 通知程序其他人 已更改数据库中的行。 这个实体也是如此 框架并不是绝对必需的 来自酒店的通知 塞特犬,你也可以确定是什么 在应用程序代码中修改 在上面调用一个显式方法 框架,以指示哪些属性 被改变了(但随后是框架 只会有一个记录 属性被修改,它不会有 原始价值)。
已编辑添加:
看来,使用EF,有一个 ObjectStateManager 可以跟踪从未真正允许断开数据的更改。为了断开数据,您必须调用ObjectContext.Detach方法来断开对象。可以找到更多here和here。
答案 3 :(得分:1)
如果你避免使用.ToList()并使用.FirstOrDefault()会怎么样?