批量更新/删除EF5

42
什么是使用(Entity Framework) EF5处理批量更新的最佳方法? 我有两个特定情况感兴趣:
  1. 为100到100,000个ID的列表(List),更新一个字段(例如UpdateDate),该列表的主键。单独调用每个更新似乎开销太大,需要很长时间。

  2. 一次性插入许多相同的对象(例如用户)(也在100到100,000之间)。

有什么好建议吗?

7个回答

37
  1. 有两个开源项目可以做到这一点:EntityFramework.Extended Entity Framework Extensions。你也可以在EF的codeplex网站上讨论关于批量更新的问题。
  2. 通过EF插入100k条记录在首位是错误的应用架构。你应该选择不同的轻量级技术进行数据导入。即使使用EF内部操作处理如此庞大的记录集也将耗费大量处理时间。目前EF没有批量插入的解决方案,但在EF的CodePlex网站上有广泛的讨论关于此功能的问题。

1
дҪ зҹҘйҒ“EntityFramework.ExtendedжҳҜеҗҰж”ҜжҢҒEF v4еҗ—пјҹ иҖҢдё”зңӢиө·жқҘEntity Framework ExtensionsжҳҜдёҖдёӘе·Із»ҸеҒңжӯўз»ҙжҠӨзҡ„йЎ№зӣ®пјҲжңҖеҗҺдёҖж¬ЎеҸ‘еёғжҳҜеңЁ2010е№ҙпјүгҖӮ - Alex Klaus
1
在 CodePlex 上有一个 EF 批量插入的扩展 https://dev59.com/_Gcs5IYBdhLWcg3wjksY#27983392 - Lukas Winzenried
批处理仍然重要吗? - Jordan

22
我看到以下选项:
1. 最简单的方法 - 手动创建SQL请求,并通过ObjectContext.ExecuteStoreCommand执行。
context.ExecuteStoreCommand("UPDATE TABLE SET FIELD1 = {0} WHERE FIELD2 = {1}", value1, value2);

2 . Use 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方法向数据库发出Update语句。
对于大量插入的情况,最好使用Bulk Copy或SSIS。EF将需要为每行插入执行单独的数据库查询。

2
使用SqlBulkCopy类来进行批量插入。请参见现有的StackOverflow Q&A,了解如何将两者集成:SqlBulkCopy 和 Entity Framework
相比于bcp(批量复制命令行实用程序)或OPEN ROWSET,SqlBulkCopy更加用户友好。

在SqlBulkCopy中,没有所谓的批量更新,只有BulkInsert。 - Pleun
我可能在回答问题的后半部分时,不小心打成了“批量更新”而不是“批量插入”。已经修正。 - John Zabroski

1
这是我成功完成的事情:
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);
}

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

0

我同意被接受的答案,即ef可能不是批量插入的正确技术。然而,我认为值得看一下EntityFramework.BulkInsert


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接