1. ホーム
  2. c#

[解決済み] Entity Frameworkにおける最速の挿入方法

2022-03-19 22:28:58

質問

Entity Frameworkに挿入する最速の方法を探しています。

これを聞いているのは、アクティブな TransactionScope で、挿入が巨大(4000以上)である。それは潜在的に10分以上(トランザクションのデフォルトタイムアウト)続く可能性があり、これは不完全なトランザクションにつながる。

解決方法は?

質問に対するコメントへ

<ブロッククオート

"...SavingChanges() 各 レコード )..."

それ、一番やっちゃいけないことですよ!?コール SaveChanges() は、各レコードの一括挿入を極端に遅くしています。私なら、いくつかの簡単なテストを行い、パフォーマンスを改善する可能性が非常に高いです。

  • コール SaveChanges() ALLレコードの後に一度だけ。
  • コール SaveChanges() を、例えば100レコード後に実行します。
  • コール SaveChanges() の後、例えば100レコードの後に、コンテキストを破棄して新しいものを作成します。
  • 変更検知を無効にする

一括挿入の場合、私はこのようなパターンで作業・実験しています。

using (TransactionScope scope = new TransactionScope())
{
    MyDbContext context = null;
    try
    {
        context = new MyDbContext();
        context.Configuration.AutoDetectChangesEnabled = false;

        int count = 0;            
        foreach (var entityToInsert in someCollectionOfEntitiesToInsert)
        {
            ++count;
            context = AddToContext(context, entityToInsert, count, 100, true);
        }

        context.SaveChanges();
    }
    finally
    {
        if (context != null)
            context.Dispose();
    }

    scope.Complete();
}

private MyDbContext AddToContext(MyDbContext context,
    Entity entity, int count, int commitCount, bool recreateContext)
{
    context.Set<Entity>().Add(entity);

    if (count % commitCount == 0)
    {
        context.SaveChanges();
        if (recreateContext)
        {
            context.Dispose();
            context = new MyDbContext();
            context.Configuration.AutoDetectChangesEnabled = false;
        }
    }

    return context;
}

560.000のエンティティ(9つのスカラプロパティ、ナビゲーションプロパティなし)をDBに挿入するテストプログラムを持っています。このコードでは、3分未満で動作します。

パフォーマンスのために重要なのは SaveChanges() は、多くのレコード(100または1000程度)の後です。また、SaveChangesの後にコンテキストを破棄し、新しいものを作成することもパフォーマンスを向上させることができます。これにより、すべてのエンタイトルからコンテキストがクリアされます。 SaveChanges ではそうならないので、エンティティはまだコンテキストにアタッチされたまま、ステート Unchanged . 段階的に挿入が遅くなるのは、コンテキストにアタッチされたエンティティのサイズが大きくなっているためです。そのため、しばらくしてからクリアするのが便利です。

以下は、私の560000エンティティの測定結果です。

  • commitCount = 1, recreateContext = false: 何時間 (現在の手順です)
  • commitCount = 100, recreateContext = falseです。 20分以上
  • commitCount = 1000, recreateContext = falseです。 242秒
  • commitCount = 10000, recreateContext = false: 202秒
  • commitCount = 100000, recreateContext = false: 199秒
  • commitCount = 1000000, recreateContext = false: メモリ不足例外
  • commitCount = 1, recreateContext = true。 10分以上
  • commitCount = 10, recreateContext = true。 241秒
  • commitCount = 100, recreateContext = true。 164秒
  • commitCount = 1000, recreateContext = true。 191秒

上記の最初のテストでの挙動は、パフォーマンスが非常に非線形で、時間とともに極端に低下することです。("Many hours" is an estimate, I never finished this test, I stopped at 50,000 entities after 20 minutes.). この非線形の挙動は、他のすべてのテストではそれほど重要ではありません。