web-dev-qa-db-ja.com

Entity Frameworkに挿入する最速の方法

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

私はあなたがアクティブなTransactionScopeを持っていて、挿入が巨大である(4000+)シナリオのためにこれを尋ねています。それは潜在的に10分(トランザクションのデフォルトのタイムアウト)以上持続することができ、これは不完全なトランザクションをもたらすでしょう。

600
Bongo Sharp

あなたの質問へのコメントであなたの発言に:

"... SavingChanges( 各レコードに対して )..."

それがあなたができる最悪のことです!各レコードに対してSaveChanges()を呼び出すと、一括挿入が極端に遅くなります。パフォーマンスを向上させる可能性が高いいくつかの簡単なテストを行います。

  • ALLレコードの後に​​一度SaveChanges()を呼び出します。
  • 例えば100レコードの後でSaveChanges()を呼び出します。
  • 例えば100レコード後にSaveChanges()を呼び出し、コンテキストを破棄して新しいコンテキストを作成します。
  • 変更検出を無効にする

バルクインサートの場合、私は次のようなパターンで作業して実験しています。

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

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

パフォーマンスのためには、 "many"レコードの後に​​SaveChanges()を呼び出すことが重要です( "many"は100または1000前後)。また、SaveChangesの後にコンテキストを破棄して新しいコンテキストを作成するパフォーマンスも向上します。これはすべてのエンティティからコンテキストをクリアします、SaveChangesはそうしません、エンティティはまだ状態Unchangedでコンテキストにアタッチされています。コンテキスト内の添付エンティティのサイズが大きくなるにつれて、挿入を段階的に遅くすることができます。そのため、しばらくしてから消去すると便利です。

これが私の560.000エンティティのいくつかの測定値です。

  • 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秒

上記の最初のテストの動作は、パフォーマンスが非常に非線形であり、時間の経過とともに極端に低下することです。 ( "何時間も"見積もりです、私はこのテストを終えたことはありません、私は20分後に50,000エンティティで停止しました。)この非線形の振る舞いは他のすべてのテストでそれほど重要ではありません。

911
Slauma

この組み合わせは十分にスピードを上げます。

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;
168
arkhivania

最速の方法は バルクインサートエクステンション を使うことです。

最高のパフォーマンスを得るためにSqlBulkCopyとカスタムデータリーダーを使用します。その結果、通常の挿入やAddRangeを使用するよりも20倍以上速くなります。 EntityFramework.BulkInsert vs EF AddRange

使い方はとても簡単

context.BulkInsert(hugeAmountOfEntities);
96
maxlego

これにはSystem.Data.SqlClient.SqlBulkCopyを使うのを見るべきです。これが ドキュメンテーション です、そしてもちろんたくさんのチュートリアルがオンラインであります。

申し訳ありませんが、あなたがEFにあなたが望むことをさせるための簡単な答えを探していたことを私は知っていますが、一括操作は実際にはORMが意味するものではありません。

73
Adam Rackis

私はAdam Rackisに同意します。 SqlBulkCopyは、あるデータソースから別のデータソースにバルクレコードを転送するための最も速い方法です。これを使って20Kレコードをコピーしましたが、3秒もかかりませんでした。以下の例を見てください。

public static void InsertIntoMembers(DataTable dataTable)
{           
    using (var connection = new SqlConnection(@"data source=;persist security info=True;user id=;password=;initial catalog=;MultipleActiveResultSets=True;App=EntityFramework"))
    {
        SqlTransaction transaction = null;
        connection.Open();
        try
        {
            transaction = connection.BeginTransaction();
            using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction))
            {
                sqlBulkCopy.DestinationTableName = "Members";
                sqlBulkCopy.ColumnMappings.Add("Firstname", "Firstname");
                sqlBulkCopy.ColumnMappings.Add("Lastname", "Lastname");
                sqlBulkCopy.ColumnMappings.Add("DOB", "DOB");
                sqlBulkCopy.ColumnMappings.Add("Gender", "Gender");
                sqlBulkCopy.ColumnMappings.Add("Email", "Email");

                sqlBulkCopy.ColumnMappings.Add("Address1", "Address1");
                sqlBulkCopy.ColumnMappings.Add("Address2", "Address2");
                sqlBulkCopy.ColumnMappings.Add("Address3", "Address3");
                sqlBulkCopy.ColumnMappings.Add("Address4", "Address4");
                sqlBulkCopy.ColumnMappings.Add("Postcode", "Postcode");

                sqlBulkCopy.ColumnMappings.Add("MobileNumber", "MobileNumber");
                sqlBulkCopy.ColumnMappings.Add("TelephoneNumber", "TelephoneNumber");

                sqlBulkCopy.ColumnMappings.Add("Deleted", "Deleted");

                sqlBulkCopy.WriteToServer(dataTable);
            }
            transaction.Commit();
        }
        catch (Exception)
        {
            transaction.Rollback();
        }

    }
}
47
Irfons

私はSlaumaの答えを調べました(これは素晴らしいです、アイデアを与えてくれた人に感謝します)。そして、最適な速度に達するまでバッチサイズを減らしました。 Slaumaの結果を見てください。

  • commitCount = 1、recreateContext = true:10分以上
  • commitCount = 10、recreateContext = true:241秒
  • commitCount = 100、recreateContext = true:164秒
  • commitCount = 1000、recreateContext = true:191秒

1から10、および10から100に移動すると速度が上がることがわかりますが、100から1000への挿入速度は再び低下しています。

だから私はあなたが10から100の間でどこかに値にバッチサイズを減らすとき何が起こっているかに焦点を合わせました、そしてここに私の結果があります(私は異なる行の内容を使っています。

Quantity    | Batch size    | Interval
1000    1   3
10000   1   34
100000  1   368

1000    5   1
10000   5   12
100000  5   133

1000    10  1
10000   10  11
100000  10  101

1000    20  1
10000   20  9
100000  20  92

1000    27  0
10000   27  9
100000  27  92

1000    30  0
10000   30  9
100000  30  92

1000    35  1
10000   35  9
100000  35  94

1000    50  1
10000   50  10
100000  50  106

1000    100 1
10000   100 14
100000  100 141

私の結果によると、実際の最適値はバッチサイズで30程度です。問題は、なぜ30が最適なのかわからないし、論理的な説明もできないことです。

18
Admir Tuzović

他の人が言っているように、本当に良い挿入パフォーマンスが必要な場合は、SqlBulkCopyがそれを行う方法です。

実装するのは少し面倒ですが、それを支援するライブラリがあります。いくつかありますが、今回は自分のライブラリを恥知らずにプラグインします: https://github.com/MikaelEliasson/EntityFramework.Utilities#batch-insert-entities

必要なコードは次のとおりです。

 using (var db = new YourDbContext())
 {
     EFBatchOperation.For(db, db.BlogPosts).InsertAll(list);
 }

それで、どれくらい速いですか?これは非常に多くの要因、コンピューターのパフォーマンス、ネットワーク、オブジェクトのサイズなどに依存するため、言うのは非常に困難です。私が行ったパフォーマンステストでは、25kエンティティをstandard waylocalhost EFUtilitiesは約300ミリ秒かかります。さらに興味深いのは、この方法を使用して15秒以内に約300万のエンティティを保存し、平均で1秒あたり約20万エンティティであるということです。

関連するデータを挿入する必要がある場合、1つの問題はもちろんです。これは、上記の方法を使用してSQLサーバーに対して効率的に実行できますが、外部キーを設定できるように、親のアプリコードでIDを生成できるID生成戦略が必要です。これは、GUIDまたはHiLo ID生成などを使用して実行できます。

17
Mikael Eliasson

EFを使用して一括挿入を行う方法に関するこの記事をお勧めします。

Entity Frameworkと低速バルクINSERT

彼はこれらの分野を探り、性能を比較します。

  1. デフォルトEF(30,000レコードの追加が完了するまで57分)
  2. ADO.NETコードに置き換える(25同じ30,000の場合)
  3. Context Bloat - 各作業単位に新しいコンテキストを使用して、アクティブなコンテキストグラフを小さくします(同じ30,000挿入に33秒かかります)。
  4. 大きなリスト - AutoDetectChangesEnabledを無効にする(約20秒に時間が短縮されます)
  5. バッチ処理(16秒まで)
  6. DbTable.AddRange() - (パフォーマンスは12の範囲内です)
17
ShaTin

Dispose()コンテキストは、あなたがAdd()エンティティがコンテキスト内の他のプリロードされたエンティティ(例えばナビゲーションプロパティ)に依存している場合に問題を引き起こします。

私は私の文脈を小さくして同じ性能を達成するために同様の概念を使用します

しかし、文脈をDispose()して再作成する代わりに、すでにSaveChanges()を持っているエンティティを単に切り離します。

public void AddAndSave<TEntity>(List<TEntity> entities) where TEntity : class {

const int CommitCount = 1000; //set your own best performance number here
int currentCount = 0;

while (currentCount < entities.Count())
{
    //make sure it don't commit more than the entities you have
    int commitCount = CommitCount;
    if ((entities.Count - currentCount) < commitCount)
        commitCount = entities.Count - currentCount;

    //e.g. Add entities [ i = 0 to 999, 1000 to 1999, ... , n to n+999... ] to conext
    for (int i = currentCount; i < (currentCount + commitCount); i++)        
        _context.Entry(entities[i]).State = System.Data.EntityState.Added;
        //same as calling _context.Set<TEntity>().Add(entities[i]);       

    //commit entities[n to n+999] to database
    _context.SaveChanges();

    //detach all entities in the context that committed to database
    //so it won't overload the context
    for (int i = currentCount; i < (currentCount + commitCount); i++)
        _context.Entry(entities[i]).State = System.Data.EntityState.Detached;

    currentCount += commitCount;
} }

必要に応じてtry catchとTrasactionScope()でラップしてください。コードをきれいに保つためにここには表示しないでください。

14
Stephen Ho

これは非常に古い質問ですが、EFで一括挿入を使用するための拡張方法を開発した人がいます。確認したところ、ライブラリの価格は599ドルであることがわかりました。たぶんそれはライブラリ全体にとって意味がありますが、一括挿入だけではこれは多すぎるのです。

これは私が作った非常に簡単な拡張方法です。私は最初にデータベースとペアでそれを使用します(最初にコードでテストされませんが、私はそれが同じように機能すると思います)。コンテキストの名前でYourEntitiesを変更します。

public partial class YourEntities : DbContext
{
    public async Task BulkInsertAllAsync<T>(IEnumerable<T> entities)
    {
        using (var conn = new SqlConnection(Database.Connection.ConnectionString))
        {
            await conn.OpenAsync();

            Type t = typeof(T);

            var bulkCopy = new SqlBulkCopy(conn)
            {
                DestinationTableName = GetTableName(t)
            };

            var table = new DataTable();

            var properties = t.GetProperties().Where(p => p.PropertyType.IsValueType || p.PropertyType == typeof(string));

            foreach (var property in properties)
            {
                Type propertyType = property.PropertyType;
                if (propertyType.IsGenericType &&
                    propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    propertyType = Nullable.GetUnderlyingType(propertyType);
                }

                table.Columns.Add(new DataColumn(property.Name, propertyType));
            }

            foreach (var entity in entities)
            {
                table.Rows.Add(
                    properties.Select(property => property.GetValue(entity, null) ?? DBNull.Value).ToArray());
            }

            bulkCopy.BulkCopyTimeout = 0;
            await bulkCopy.WriteToServerAsync(table);
        }
    }

    public void BulkInsertAll<T>(IEnumerable<T> entities)
    {
        using (var conn = new SqlConnection(Database.Connection.ConnectionString))
        {
            conn.Open();

            Type t = typeof(T);

            var bulkCopy = new SqlBulkCopy(conn)
            {
                DestinationTableName = GetTableName(t)
            };

            var table = new DataTable();

            var properties = t.GetProperties().Where(p => p.PropertyType.IsValueType || p.PropertyType == typeof(string));

            foreach (var property in properties)
            {
                Type propertyType = property.PropertyType;
                if (propertyType.IsGenericType &&
                    propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    propertyType = Nullable.GetUnderlyingType(propertyType);
                }

                table.Columns.Add(new DataColumn(property.Name, propertyType));
            }

            foreach (var entity in entities)
            {
                table.Rows.Add(
                    properties.Select(property => property.GetValue(entity, null) ?? DBNull.Value).ToArray());
            }

            bulkCopy.BulkCopyTimeout = 0;
            bulkCopy.WriteToServer(table);
        }
    }

    public string GetTableName(Type type)
    {
        var metadata = ((IObjectContextAdapter)this).ObjectContext.MetadataWorkspace;
        var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace));

        var entityType = metadata
                .GetItems<EntityType>(DataSpace.OSpace)
                .Single(e => objectItemCollection.GetClrType(e) == type);

        var entitySet = metadata
            .GetItems<EntityContainer>(DataSpace.CSpace)
            .Single()
            .EntitySets
            .Single(s => s.ElementType.Name == entityType.Name);

        var mapping = metadata.GetItems<EntityContainerMapping>(DataSpace.CSSpace)
                .Single()
                .EntitySetMappings
                .Single(s => s.EntitySet == entitySet);

        var table = mapping
            .EntityTypeMappings.Single()
            .Fragments.Single()
            .StoreEntitySet;

        return (string)table.MetadataProperties["Table"].Value ?? table.Name;
    }
}

そのようにIEnumerableから継承する任意のコレクションに対してそれを使うことができます:

await context.BulkInsertAllAsync(items);
7
Guilherme

挿入したいデータのXMLを取得する ストアドプロシージャ を使用してみてください。

5
Maxim

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

Bulk Insertをサポートするサードパーティのライブラリがいくつかあります。

  • Z.EntityFramework.Extensions(推奨
  • EFUtilities
  • EntityFramework.BulkInsert

参照項目: Entity Framework一括挿入ライブラリ

一括挿入ライブラリを選択するときは注意してください。EntityFramework Extensionsのみがあらゆる種類の関連付けと継承をサポートしています。まだサポートされているのはこれだけです。


免責事項 :私は Entity Framework Extensions の所有者です

このライブラリを使用すると、シナリオに必要なすべての一括操作を実行できます。

  • 一括保存の変更
  • 一括挿入
  • 一括削除
  • 一括更新
  • 一括マージ

// Easy to use
context.BulkSaveChanges();

// Easy to customize
context.BulkSaveChanges(bulk => bulk.BatchSize = 100);

// Perform Bulk Operations
context.BulkDelete(customers);
context.BulkInsert(customers);
context.BulkUpdate(customers);

// Customize Primary Key
context.BulkMerge(customers, operation => {
   operation.ColumnPrimaryKeyExpression = 
        customer => customer.Code;
});
4
Jonathan Magnan

上記の@Slaumaの例を一般的に拡張しました。

public static class DataExtensions
{
    public static DbContext AddToContext<T>(this DbContext context, object entity, int count, int commitCount, bool recreateContext, Func<DbContext> contextCreator)
    {
        context.Set(typeof(T)).Add((T)entity);

        if (count % commitCount == 0)
        {
            context.SaveChanges();
            if (recreateContext)
            {
                context.Dispose();
                context = contextCreator.Invoke();
                context.Configuration.AutoDetectChangesEnabled = false;
            }
        }
        return context;
    }
}

使用法:

public void AddEntities(List<YourEntity> entities)
{
    using (var transactionScope = new TransactionScope())
    {
        DbContext context = new YourContext();
        int count = 0;
        foreach (var entity in entities)
        {
            ++count;
            context = context.AddToContext<TenancyNote>(entity, count, 100, true,
                () => new YourContext());
        }
        context.SaveChanges();
        transactionScope.Complete();
    }
}
4
Sgedda

リストを保存する最も速い方法の1つはあなたが次のコードを適用しなければなりません

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;

AutoDetectChangesEnabled = false

追加、追加範囲および保存変更:変更を検出しません。

ValidateOnSaveEnabled = false;

変更追跡を検出しない

あなたはnugetを追加しなければなりません

Install-Package Z.EntityFramework.Extensions

今、あなたは次のコードを使用することができます

var context = new MyContext();

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;

context.BulkInsert(list);
context.BulkSaveChanges();
4
Reza Jenabi

それがここで言及されなかったように私はEFCore.BulkExtensionsを推薦したいです/ ここ

context.BulkInsert(entitiesList);                 context.BulkInsertAsync(entitiesList);
context.BulkUpdate(entitiesList);                 context.BulkUpdateAsync(entitiesList);
context.BulkDelete(entitiesList);                 context.BulkDeleteAsync(entitiesList);
context.BulkInsertOrUpdate(entitiesList);         context.BulkInsertOrUpdateAsync(entitiesList);         // Upsert
context.BulkInsertOrUpdateOrDelete(entitiesList); context.BulkInsertOrUpdateOrDeleteAsync(entitiesList); // Sync
context.BulkRead(entitiesList);                   context.BulkReadAsync(entitiesList);
2
Manfred Wippel

SqlBulkCopyを使う:

void BulkInsert(GpsReceiverTrack[] gpsReceiverTracks)
{
    if (gpsReceiverTracks == null)
    {
        throw new ArgumentNullException(nameof(gpsReceiverTracks));
    }

    DataTable dataTable = new DataTable("GpsReceiverTracks");
    dataTable.Columns.Add("ID", typeof(int));
    dataTable.Columns.Add("DownloadedTrackID", typeof(int));
    dataTable.Columns.Add("Time", typeof(TimeSpan));
    dataTable.Columns.Add("Latitude", typeof(double));
    dataTable.Columns.Add("Longitude", typeof(double));
    dataTable.Columns.Add("Altitude", typeof(double));

    for (int i = 0; i < gpsReceiverTracks.Length; i++)
    {
        dataTable.Rows.Add
        (
            new object[]
            {
                    gpsReceiverTracks[i].ID,
                    gpsReceiverTracks[i].DownloadedTrackID,
                    gpsReceiverTracks[i].Time,
                    gpsReceiverTracks[i].Latitude,
                    gpsReceiverTracks[i].Longitude,
                    gpsReceiverTracks[i].Altitude
            }
        );
    }

    string connectionString = (new TeamTrackerEntities()).Database.Connection.ConnectionString;
    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();
        using (var transaction = connection.BeginTransaction())
        {
            using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction))
            {
                sqlBulkCopy.DestinationTableName = dataTable.TableName;
                foreach (DataColumn column in dataTable.Columns)
                {
                    sqlBulkCopy.ColumnMappings.Add(column.ColumnName, column.ColumnName);
                }

                sqlBulkCopy.WriteToServer(dataTable);
            }
            transaction.Commit();
        }
    }

    return;
}
2
Amir Saniyan

別の選択肢は、Nugetから入手可能なSqlBulkToolsを使用することです。使い方はとても簡単で、いくつかの強力な機能があります。

例:

var bulk = new BulkOperations();
var books = GetBooks();

using (TransactionScope trans = new TransactionScope())
{
    using (SqlConnection conn = new SqlConnection(ConfigurationManager
    .ConnectionStrings["SqlBulkToolsTest"].ConnectionString))
    {
        bulk.Setup<Book>()
            .ForCollection(books)
            .WithTable("Books") 
            .AddAllColumns()
            .BulkInsert()
            .Commit(conn);
    }

    trans.Complete();
}

より多くの例と高度な用法については ドキュメント を見てください。免責事項:私はこの図書館の著者であり、いかなる見解も私自身の意見です。

2
Greg R Taylor

これは、Entity Frameworkを使用した場合とSqlBulkCopyクラスを使用した場合の実際的な例のパフォーマンス比較です。 複雑なオブジェクトをSQL Serverデータベースに一括挿入する方法

他の人がすでに強調しているように、ORMは一括操作で使用されることを意図していません。柔軟性、懸念の分離、その他の利点がありますが、一括操作(一括読み取りを除く)はその1つではありません。

2
Zoran Horvat

あなたはバックグラウンドワーカーやタスクを介して挿入しようとしたことがありますか?

私の場合は、(NavigationPropertiesによる)外部キー関係を持つ182の異なるテーブルに分散された7760のレジスタを挿入するimです。

タスクがなければ、2分半かかりました。タスク(Task.Factory.StartNew(...))内に、15秒かかりました。

すべてのエンティティをコンテキストに追加した後にのみSaveChanges()を実行します。 (データの完全性を確保するため)

1
Rafael A. M. S.

SaveChanges()を実行すると、insertステートメントが1つずつデータベースに送信されるため、Entityが機能するため、ここに記載されているすべての解決策は役に立ちません。

たとえば、データベースへの往復の時間が50ミリ秒の場合、挿入に必要な時間はレコード数x 50ミリ秒です。

あなたはBulkInsertを使用する必要があります、ここにリンクがあります: https://efbulkinsert.codeplex.com/ /

私はそれを使用することによって挿入時間を5〜6分から10〜12秒に短縮しました。

1
Aleksa

SqlBulkCopyは超高速です

これは私の実装です:

// at some point in my calling code, I will call:
var myDataTable = CreateMyDataTable();
myDataTable.Rows.Add(Guid.NewGuid,tableHeaderId,theName,theValue); // e.g. - need this call for each row to insert

var efConnectionString = ConfigurationManager.ConnectionStrings["MyWebConfigEfConnection"].ConnectionString;
var efConnectionStringBuilder = new EntityConnectionStringBuilder(efConnectionString);
var connectionString = efConnectionStringBuilder.ProviderConnectionString;
BulkInsert(connectionString, myDataTable);

private DataTable CreateMyDataTable()
{
    var myDataTable = new DataTable { TableName = "MyTable"};
// this table has an identity column - don't need to specify that
    myDataTable.Columns.Add("MyTableRecordGuid", typeof(Guid));
    myDataTable.Columns.Add("MyTableHeaderId", typeof(int));
    myDataTable.Columns.Add("ColumnName", typeof(string));
    myDataTable.Columns.Add("ColumnValue", typeof(string));
    return myDataTable;
}

private void BulkInsert(string connectionString, DataTable dataTable)
{
    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();
        SqlTransaction transaction = null;
        try
        {
            transaction = connection.BeginTransaction();

            using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction))
            {
                sqlBulkCopy.DestinationTableName = dataTable.TableName;
                foreach (DataColumn column in dataTable.Columns) {
                    sqlBulkCopy.ColumnMappings.Add(column.ColumnName, column.ColumnName);
                }

                sqlBulkCopy.WriteToServer(dataTable);
            }
            transaction.Commit();
        }
        catch (Exception)
        {
            transaction?.Rollback();
            throw;
        }
    }
}
1
Philip Johnson

私の知る限りでは、巨大なインサートのパフォーマンスを向上させるためにEntityFrameworkno BulkInsertがあります。

このシナリオでは、ADO.net内に SqlBulkCopy を指定して問題を解決できます。

1
anishMarokey

[POSTGRESQLの新しい解決策]これはかなり古い投稿ですが、最近同様の問題にぶつかったのですが、Postgresqlを使用していました。私は効果的なbulkinsertを使いたかった、それは結構難しいことがわかった。私はこのDB上でそうするための適切な無料のライブラリを見つけていません。私はこのヘルパーだけを見つけました: https://bytefish.de/blog/postgresql_bulk_insert/ Nugetにもあります。私はEntity Frameworkの方法でプロパティを自動マッピングする小さなマッパーを書きました。

public static PostgreSQLCopyHelper<T> CreateHelper<T>(string schemaName, string tableName)
        {
            var helper = new PostgreSQLCopyHelper<T>("dbo", "\"" + tableName + "\"");
            var properties = typeof(T).GetProperties();
            foreach(var prop in properties)
            {
                var type = prop.PropertyType;
                if (Attribute.IsDefined(prop, typeof(KeyAttribute)) || Attribute.IsDefined(prop, typeof(ForeignKeyAttribute)))
                    continue;
                switch (type)
                {
                    case Type intType when intType == typeof(int) || intType == typeof(int?):
                        {
                            helper = helper.MapInteger("\"" + prop.Name + "\"",  x => (int?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type stringType when stringType == typeof(string):
                        {
                            helper = helper.MapText("\"" + prop.Name + "\"", x => (string)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type dateType when dateType == typeof(DateTime) || dateType == typeof(DateTime?):
                        {
                            helper = helper.MapTimeStamp("\"" + prop.Name + "\"", x => (DateTime?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type decimalType when decimalType == typeof(decimal) || decimalType == typeof(decimal?):
                        {
                            helper = helper.MapMoney("\"" + prop.Name + "\"", x => (decimal?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type doubleType when doubleType == typeof(double) || doubleType == typeof(double?):
                        {
                            helper = helper.MapDouble("\"" + prop.Name + "\"", x => (double?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type floatType when floatType == typeof(float) || floatType == typeof(float?):
                        {
                            helper = helper.MapReal("\"" + prop.Name + "\"", x => (float?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type guidType when guidType == typeof(Guid):
                        {
                            helper = helper.MapUUID("\"" + prop.Name + "\"", x => (Guid)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                }
            }
            return helper;
        }

私はそれを次のように使います(私はUndertakingという名前のエンティティを持っていました):

var undertakingHelper = BulkMapper.CreateHelper<Model.Undertaking>("dbo", nameof(Model.Undertaking));
undertakingHelper.SaveAll(transaction.UnderlyingTransaction.Connection as Npgsql.NpgsqlConnection, undertakingsToAdd));

トランザクションの例を示しましたが、コンテキストから取得した通常の接続でも実行できます。 undertakingsToAddは通常のエンティティレコードを列挙できます。これをbulkInsertしてDBに格納します。

私が数時間の調査と試行の後に得たこの解決策は、あなたがはるかに速くそして最終的に使いやすくそして無料であることを期待できるからです!上記の理由だけでなく、Postgresql自体に問題がなかった唯一のものであるため、このソリューションを使用することをお勧めします。他の多くのソリューションは、SqlServerなどで完璧に機能します。

1
Michał Pilarek

Bulk package libraryを使うことができます。一括挿入1.0.0バージョンは、Entity framework> = 6.0.0を持つプロジェクトで使用されます。

より多くの説明はここで見つけることができます - Bulkoperationソースコード

1
Mohd Nadeem

しかし、(+ 4000)を超える挿入にはストアドプロシージャを使用することをお勧めします。経過時間を添付した。私はそれを20インチに11.788行挿入しました enter image description here

それはコードだ

 public void InsertDataBase(MyEntity entity)
    {
        repository.Database.ExecuteSqlCommand("sp_mystored " +
                "@param1, @param2"
                 new SqlParameter("@param1", entity.property1),
                 new SqlParameter("@param2", entity.property2));
    }
0
Marinpietri

その秘密は、同じ空白のステージングテーブルに挿入することです。インサートは急速に軽量化しています。それから single insertをあなたのメインの大きなテーブルに実行してください。その後、次のバッチに備えてステージングテーブルを切り捨てます。

すなわち。

insert into some_staging_table using Entity Framework.

-- Single insert into main table (this could be a tiny stored proc call)
insert into some_main_already_large_table (columns...)
   select (columns...) from some_staging_table
truncate table some_staging_table
0
Simon Hughes

データを挿入するには、xml形式の入力データを受け取るストアドプロシージャを使用します。

あなたのC#コードからXMLとして挿入データを渡します。

例えばc#では、構文は次のようになります。

object id_application = db.ExecuteScalar("procSaveApplication", xml)
0
arun tiwari