3

Entity Framework を SQL Server Compact Edition で使用すると、メモリ リークが発生します。私の状況:

約600MByteのファイルがあります。私はそれを 1 行ずつ読み、エンティティ クラスを作成し、それを SQL Server CE データベースに追加しました。それによってメモリは非常に急速に成長しています。Gen 0 コレクション カウンターと Gen 2 ヒープ サイズが非常に急速に増加しています (Process Explorer からの情報)。私が正しく理解していれば、Gen 2ヒープは大きなオブジェクト用です。私のエンティティクラスは大きなオブジェクトだと思います。したがって、Entity Framework はオブジェクトを保存し、解放しません。私はすでにそれらを切り離して GC.Collect(2) を呼び出そうとしていますが、役に立ちません。

最初に行を読みました。次に、行を解析した後にオブジェクトを作成します。次に、DB に追加します。ここに私のデータベースコードがあります:

DBEntities dbConnection = new DBEntities();
dbConnection.My_Table.AddObject(MyObjectCreatedFromTheLine);
dbConnection.SaveChanges();
//  dbConnection.Detach(MyObjectCreatedFromTheLine);
//  dbConnection.Dispose();
MyObjectCreatedFromTheLine = null;
dbConnection = null;

また、作成されたエンティティ クラス ( MyObjectCreatedFromTheLine) が に属していると読みましたDbContext。したがって、すべての行に対してこのコードを呼び出し、毎回新しいコンテキストを作成します。

私は何を間違っていますか?

4

4 に答える 4

4

エンティティ フレームワークを使用して 50,000 件以上のレコードを SQL データベースに挿入しようとして、この問題に遭遇しました。エンティティ フレームワークは大規模な一括操作 (大規模な挿入または削除操作) 向けではないため、最終的に System.Data.SqlClient.SqlBulkCopy ライブラリを使用することになりました。これははるかに効率的で高速です。以下のヘルパー関数を自動マップに記述したので、SQL Insert ステートメントを手動で作成する必要はありませんでした。(それはわずかに型に依存しません!と思います)。

基本的にワークフローは次のとおりです: IList<MyEntityType> -> DataTable -> SqlBulkCopy

public static void BulkInsert<T>(string connection, string tableName, IList<T> list)
    {
        using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepNulls))
        {
            bulkCopy.BatchSize = list.Count;
            bulkCopy.DestinationTableName = tableName;
            bulkCopy.BulkCopyTimeout = 3000;

            var table = new DataTable();
            var props = TypeDescriptor.GetProperties(typeof(T))
                //Dirty hack to make sure we only have system data types 
                //i.e. filter out the relationships/collections
                                       .Cast<PropertyDescriptor>()
                                       .Where(propertyInfo => propertyInfo.PropertyType.Namespace.Equals("System"))
                                       .ToArray();

            foreach (var propertyInfo in props)
            {
                bulkCopy.ColumnMappings.Add(propertyInfo.Name, propertyInfo.Name);
                table.Columns.Add(propertyInfo.Name, Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType);
            }

            var values = new object[props.Length];
            foreach (var item in list)
            {
                for (var i = 0; i < values.Length; i++)
                {
                    values[i] = props[i].GetValue(item);
                }

                table.Rows.Add(values);
            }

            bulkCopy.WriteToServer(table);
        }
    }

私の例では、挿入に 15 ~ 20 分かかっていましたが、1 分未満になりました。

于 2012-07-12T14:28:06.980 に答える
0

データコンテキストを介して膨大な数のエンティティを追加することが最善の方法ではないと思います。データコンテキストには、コンテキストが破棄されるまでオブジェクトが残る内部第1レベルのキャッシュがあるため、作成されたオブジェクトごとにメモリを消費します。

私はEFをよく知りませんし、単一のオブジェクトを永続化するたびにキャッシュをクリアできるかどうかもわかりません。ただし、大規模な挿入を実行するためにEFをまったく使用しないことを選択したいと思います。

代わりに、SqlBulkCopyクラスを使用してください。これにより、メモリの問題が解決されるはずです。また、EFおよびオブジェクトごとの挿入で実現できるものよりも1桁高速です。

于 2012-07-12T12:51:51.777 に答える
0

あなたのアプローチは正しくないと思います。オブジェクトを1 つ作成するだけDBEntitiesで、すべての変更を保存できます。次のようなものがうまくいくかもしれません。

using(DBEntities dbConnection = new DBEntities())
{
    foreach(MyObjectCreatedFromTheLine entity in ListOfMyObjectCreatedFromTheLine)
    {
        dbConnection.My_Table.AddObject(MyObjectCreatedFromTheLine);
    }
    dbConnection.SaveChanges();
}

DBEntitiesエンティティごとに新しいオブジェクトを作成していますが、これは正しくありません。dbConnection を null に設定するだけでは、オブジェクトが破棄されたり、ガベージ コレクターがオブジェクトを収集しないとは限りません。実際、参照を null に設定しているだけで、オブジェクトはまだメモリ内にあり、ガベージ コレクターがオブジェクトを収集します。

于 2012-07-12T11:46:27.790 に答える
0

DBEntities dbConnection = new DBEntities() をループから外します!?

反復ごとに新しいオブジェクト コンテキストを作成することは、ばかげているだけでなく、無関係です。

また、特にそのような大きなオブジェクトの場合、割り当てに時間がかかります。おそらく問題であるメモリのオーバーヘッドと割り当て解除は言うまでもありません。

于 2013-06-27T09:19:51.600 に答える