【问题标题】:EF4 update a value for all rows in a table without doing a selectEF4 更新表中所有行的值而不进行选择
【发布时间】:2011-02-19 19:48:01
【问题描述】:

我需要在运行更新之前重置特定表中的布尔字段。 该表可能有 100 万条左右的记录,我不希望在更新前进行选择,因为它需要太多时间。

基本上我在代码中需要的是在 TSQL 中生成以下内容

update tablename 
set flag = false 
where flag = true

我有一些接近我需要的东西http://www.aneyfamily.com/terryandann/post/2008/04/Batch-Updates-and-Deletes-with-LINQ-to-SQL.aspx 但尚未实施,但想知道是否有更标准的方法。

为了保持我们对该项目的限制,我们不能使用 SPROC 或直接在上下文中的 ExecuteStoreCommand 参数中编写 TSQL,我相信你可以做到。

我知道我需要做的事情可能不会在 EF4 中得到直接支持,我们可能需要查看 SPROC 来完成这项工作 [完全没有任何其他方式],但我只需要充分探索所有先说可能性。 在 EF 理想世界中,可以调用上面的更新标志,或者可以仅获取具有 id 和布尔标志的实体减去关联的实体并循环遍历实体并设置标志并执行单个SaveChanges 调用,但这可能不是它的工作方式。

任何想法,

提前致谢。 利亚姆

【问题讨论】:

  • “因为它花费了太多时间” - 多长时间?
  • 1-2 分钟,即使是 30k 行的测试数据。我正在更新的实体有 4 个相关表,因此加载这些数据可能会产生影响。我想我想要实现的理想性能是直接在数据库上运行更新会给出什么 - 虽然不确定它是否可能
  • 只需要更新一张桌子吗?
  • 当它是一个完美的解决方案时却不能使用 sprocs 是不幸的——为什么要制定这条规则?
  • 这是应用程序的一个强有力的指导方针,我们正在努力确保业务规则完全保留在我们的业务层中,并且它们不会在整个生命周期内缓慢迁移到 DB 层应用程序,因为不同的开发人员在应用程序上工作。我们认为这种方法也将有助于应用程序的可测试性。此外,如果没有替代方案,我们将不得不考虑针对此类情况的 SPROC,但希望在这样做之前充分探索 EF 中的替代方案。感谢您的评论。

标签: c# sql sql-server entity-framework-4


【解决方案1】:

我会去找利益相关者,他们介绍了不直接使用 SQL 或 SProc 的限制,并向他展示以下事实:

  • ORM 中的更新(如实体框架)以这种方式工作:加载对象、执行修改、保存对象。这是唯一有效的方法。
  • 显然,在您的情况下,这意味着加载 1M 实体并分别执行 1M 更新(EF 没有命令批处理 - 每个命令都在自己的往返 DB 中运行)- 通常绝对无用的解决方案。
  • 您提供的示例看起来很有趣,但它是针对 Linq-To-Sql 的。不适用于实体框架。除非您实施它,否则您无法确定它是否适用于 EF,因为 EF 中的基础架构要复杂得多。因此,您可能会花费数天时间来执行此操作而没有任何结果 - 这应该得到利益相关者的批准。
  • 使用 SProc 或直接 SQL 的解决方案将花费您几分钟的时间,而且很简单。
  • 在这两种解决方案中,您都必须处理另一个问题。如果您已经拥有实体化实体并且您将运行此类命令(通过上述扩展或通过 SQL)这些更改将不会反映在已加载的实体中 - 您必须对其进行迭代并设置标志。
  • 这两种情况都会中断工作单元,因为在工作单元完成之前执行了一些数据更改。

关键在于使用正确的工具来满足正确的需求。

顺便说一句。可以避免加载相关表。这只是关于您运行的查询。不要使用 Include 并且不要访问导航属性(在延迟加载的情况下),您将不会加载关系。

可以仅选择 Id(通过投影),创建虚拟实体(仅将 id 和 flag 设置为 true)并仅执行 flag 更新,但仍会执行多达 1M 次更新。

using(var myContext = new MyContext(connectionString))
{
  var query = from o in myContext.MyEntities
              where o.Flag == false
              select o.Id;
  foreach (var id in query)
  {
    var entity = new MyEntity
      {
        Id = id,
        Flag = true
      };
    myContext.Attach(entity);
    myContext.ObjectStateManager.GetObjectStateEntry(entity).SetModifiedProperty("Flag");
  }

  myContext.SaveChanges();
}

此外,它只能在空对象上下文中工作(或者至少更新表中的任何实体都不能附加到上下文中)。因此,在某些情况下,在其他更新之前运行它需要两个 ObjectContext 实例 = 手动共享 DbConnection 或两个数据库连接,并且在事务的情况下 = 分布式事务和另一个性能损失。

【讨论】:

  • 感谢您提供全面、清晰和深思熟虑的回答。
【解决方案2】:

创建一个新的 EF 模型,并且只添加您需要在其上进行更新的一个表。这样,所有的连接都不会发生。这将大大加快您的处理速度。

【讨论】:

  • 谢谢,但是我们正试图坚持使用一个模型,这种情况可能会发生在许多表中,因此我们不希望为每个类似案例都提供模型。
【解决方案3】:
ObjectContext.ExecuteStoreCommand ( _
    commandText As String, _
    ParamArray parameters As Object() _
    ) As Integer

http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.executestorecommand.aspx

编辑
抱歉,没有看完帖子。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-29
    • 1970-01-01
    • 1970-01-01
    • 2018-05-27
    • 1970-01-01
    • 2020-12-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多