【问题标题】:Batch update/delete EF5批量更新/删除EF5
【发布时间】:2012-09-26 21:43:44
【问题描述】:

使用(实体框架)EF5 处理批量更新的最佳方法是什么? 我有 2 个我感兴趣的特殊案例:

  1. 更新 100 到 100.000 个 Id 之间的列表 (List) 的字段(例如 UpdateDate),该列表是主键。单独调用每个更新似乎开销很大并且需要很长时间。

  2. 一次性插入许多相同的对象(例如用户),也介于 100 和 100.000 之间。

有什么好的建议吗?

【问题讨论】:

    标签: entity-framework batch-processing entity-framework-5


    【解决方案1】:
    1. 有两个开源项目允许这样做:EntityFramework.ExtendedEntity Framework Extensions。您还可以在 EF 的 codeplex 网站上查看 discussion 关于批量更新的信息。
    2. 通过 EF 插入 100k 记录首先是错误的应用程序架构。您应该为数据导入选择不同的轻量级技术。即使是 EF 的内部操作,拥有如此庞大的记录集,也会花费您大量的处理时间。目前没有针对 EF 批量插入的解决方案,但在 EF 的代码 plex 站点上有关于此功能的 broad discussion

    【讨论】:

    • 你知道在EntityFramework.Extended中支持EF v4吗?而且似乎 Entity Framework Extensions 是死项目(最后一个版本是在 2010 年)
    • 在 codeplex stackoverflow.com/a/27983392/937411 上有一个 ef bulk inserts 的扩展
    • 批处理还重要吗?
    【解决方案2】:

    我看到以下选项:

    1 .最简单的方法——手动创建你的SQL请求并通过ObjectContext.ExecuteStoreCommand执行

    context.ExecuteStoreCommand("UPDATE TABLE SET FIELD1 = {0} WHERE FIELD2 = {1}", value1, value2);
    

    2 。使用EntityFramework.Extended

    context.Tasks.Update(
        t => t.StatusId == 1, 
        t => new Task {StatusId = 2});
    

    3 .为 EF 创建自己的扩展。有一篇文章Bulk Delete,通过继承ObjectContext类实现了这个目标。值得一看。批量插入/更新可以以相同的方式实现。

    【讨论】:

      【解决方案3】:

      您可能不想听到它,但最好的选择是不要将 EF 用于批量操作。要跨记录表更新字段,请在数据库中使用 Update 语句(可能通过映射到 EF 函数的存储过程调用)。您还可以使用 Context.ExecuteStoreQuery 方法向数据库发出更新语句。

      对于大量插入,最好的办法是使用批量复制或 SSIS。 EF 将要求为插入的每一行单独命中数据库。

      【讨论】:

        【解决方案4】:

        应该使用 SqlBulkCopy 类来完成批量插入。请参阅已有的 StackOverflow Q&A 关于整合两者:SqlBulkCopy and Entity Framework

        SqlBulkCopy 比 bcp(批量复制命令行实用程序)甚至 OPEN ROWSET 更加用户友好。

        【讨论】:

        • 没有使用 SqlBulkCopy 的批量更新,只有 BulkInsert
        • 我可能正在回答问题的后半部分,并且不小心输入了“批量更新”而不是“批量插入”。固定。
        【解决方案5】:
            public static bool BulkDelete(string tableName, string columnName, List<object> val)
            {
                bool ret = true;
        
                var max = 2000;
                var pages = Math.Ceiling((double)val.Count / max);
                for (int i = 0; i < pages; i++)
                {
                    var count = max;
                    if (i == pages - 1) { count = val.Count % max; }
        
                    var args = val.GetRange(i * max, count);
                    var cond = string.Join("", args.Select((t, index) => $",@p{index}")).Substring(1);
                    var sql = $"DELETE FROM {tableName} WHERE {columnName} IN ({cond}) ";
        
                    ret &= Db.ExecuteSqlCommand(sql, args.ToArray()) > 0;
                }
        
                return ret;
            }
        

        【讨论】:

          【解决方案6】:

          我同意公认的答案,即 ef 可能是批量插入的错误技术。 不过,我认为值得一看 EntityFramework.BulkInsert

          【讨论】:

            【解决方案7】:

            这是我成功完成的:

            private void BulkUpdate()
            {
                var oc = ((IObjectContextAdapter)_dbContext).ObjectContext;
                var updateQuery = myIQueryable.ToString(); // This MUST be above the call to get the parameters.
                var updateParams = GetSqlParametersForIQueryable(updateQuery).ToArray();
                var updateSql = $@"UPDATE dbo.myTable
                                   SET col1 = x.alias2
                                   FROM dbo.myTable
                                   JOIN ({updateQuery}) x(alias1, alias2) ON x.alias1 = dbo.myTable.Id";
                oc.ExecuteStoreCommand(updateSql, updateParams);
            }
            
            private void BulkInsert()
            {
                var oc = ((IObjectContextAdapter)_dbContext).ObjectContext;
                var insertQuery = myIQueryable.ToString(); // This MUST be above the call to get the parameters.
                var insertParams = GetSqlParametersForIQueryable(insertQuery).ToArray();
                var insertSql = $@"INSERT INTO dbo.myTable (col1, col2)
                                   SELECT x.alias1, x.alias2
                                   FROM ({insertQuery}) x(alias1, alias2)";
                oc.ExecuteStoreCommand(insertSql, insertParams.ToArray());
            }    
            
            private static IEnumerable<SqlParameter> GetSqlParametersForIQueryable<T>(IQueryable<T> queryable)
            {
                var objectQuery = GetObjectQueryFromIQueryable(queryable);
                return objectQuery.Parameters.Select(x => new SqlParameter(x.Name, x.Value));
            }
            
            private static ObjectQuery<T> GetObjectQueryFromIQueryable<T>(IQueryable<T> queryable)
            {
                var dbQuery = (DbQuery<T>)queryable;
                var iqProp = dbQuery.GetType().GetProperty("InternalQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
                var iq = iqProp.GetValue(dbQuery, null);
                var oqProp = iq.GetType().GetProperty("ObjectQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
                return (ObjectQuery<T>)oqProp.GetValue(iq, null);
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-11-23
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-03-15
              • 1970-01-01
              相关资源
              最近更新 更多