批量更新/删除EF5

时间:2012-10-05 17:32:05

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

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

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

  2. 一次性插入多个,也就是100到100.000之间的相同对象(例如用户)。

  3. 有什么好建议吗?

7 个答案:

答案 0 :(得分:36)

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

答案 1 :(得分:21)

我看到以下选项:

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 类来实现的。值得一看。批量插入/更新可以以相同的方式实现。

答案 2 :(得分:3)

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

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

答案 3 :(得分:2)

应使用SqlBulkCopy类完成批量插入。请参阅预先存在的StackOverflow Q& A关于集成两者:SqlBulkCopy and Entity Framework

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

答案 4 :(得分:1)

    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;
    }

答案 5 :(得分:0)

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

答案 6 :(得分:0)

以下是我成功完成的事情:

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);
}
相关问题