web-dev-qa-db-ja.com

EF5のバッチ更新/削除

(Entity Framework)EF5を使用してバッチ更新を処理する最良の方法は何ですか?興味のある2つの特定のケースがあります。

  1. 主キーである100〜100.000 IDのリスト(リスト)のフィールド(UpdateDateなど)を更新します。各更新プログラムを個別に呼び出すと、オーバーヘッドが大きくなり、時間がかかります。

  2. 同じオブジェクト(ユーザーなど)を一度に100〜100.000の間で挿入します。

良いアドバイスはありますか?

41
Frank
  1. これを可能にする2つのオープンソースプロジェクトがあります: EntityFramework.Extended および Entity Framework Extensions 。 EFのコードプレックスサイトでの一括更新について discussion も確認できます。
  2. EFを介して10万件のレコードを挿入することは、そもそも間違ったアプリケーションアーキテクチャです。データのインポートには、異なる軽量テクノロジーを選択する必要があります。このような大きなレコードセットを使用したEFの内部操作でさえ、多くの処理時間がかかります。現在、EFのバッチ挿入の解決策はありませんが、EFのコードプレックスサイトには、この機能に関する 広範な議論 があります。
36
Ladislav Mrnka

次のオプションが表示されます。

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用の独自の拡張機能を作成します。記事があります 一括削除 この目標はObjectContextクラスを継承することで達成されました。見てみる価値があります。一括挿入/更新も同じ方法で実装できます。

21
Alex Klaus

聞きたくないかもしれませんが、最良の選択肢はバルク操作にEFを使用しないことです。レコードのテーブル全体でフィールドを更新するには、データベースでUpdateステートメントを使用します(EF関数にマップされたストアドプロシージャを介して呼び出される場合があります)。 Context.ExecuteStoreQueryメソッドを使用して、データベースにUpdateステートメントを発行することもできます。

大量の挿入の場合、最善の策は一括コピーまたはSSISを使用することです。 EFでは、挿入される行ごとにデータベースへの個別のヒットが必要になります。

3
Jim Wooley

一括挿入は、SqlBulkCopyクラスを使用して行う必要があります。 2つの統合に関する既存のStackOverflow Q&Aを参照してください: SqlBulkCopyおよびEntity Framework

SqlBulkCopyは、bcp(一括コピーコマンドラインユーティリティ)やOPEN ROWSETよりもはるかにユーザーフレンドリーです。

2
John Zabroski
    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;
    }
1
Jack CQ

Efはおそらく一括挿入の間違った技術であるという受け入れられた答えに同意します。ただし、 EntityFramework.BulkInsert を見る価値があると思います。

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);
}
0
adam0101