3

EntityFramework 4 を使用しています。EntityCollection<SomeEntity> currentEntities~500k のエンティティとList<SomeEntity> importedEntities~500k のレコードがあります。currentEntitiesに存在しない で発生するすべてのレコードのリストが必要importedEntitiesです。

currentEntities.Select(x => x.ID).Except(importedEntities.Select(x => x.ID))発生するレコードの一意の ID を取得するために呼び出すとSystem.OutOfMemoryException、明らかにすべてのエンティティがメモリに読み込まれるため、 a が発生します。

呼び出しcurrentEntities.Where(x => !importedEntities.Any(y => y.ID == x.ID))は失敗しますNotSupportedException(「このコンテキストでは、プリミティブ型 ('Int32、String、および Guid' など) のみがサポートされています」)。

currentEntitiesSQL Server 2008 R2 データベース上にありimportedEntities、メモリ内にあります。

これは L2E でも可能ですか?

4

4 に答える 4

2

メモリ内の整数exceptの2つの「大きな」リストを実行することはポイントではありません。実行Enumerable.Range(0, 500000).Except(Enumerable.Range(500, 500000)).Count()すると、OutOfMemoryException と言う前に 500 が返されます。したがって、問題を整数の に減らすとexceptうまくいくはずです。

var newIds = importedEntities.Select(x => x.ID).ToArray()
             .Except(currentEntities.Select(x => x.ID).ToArray()).ToArray();

したがって、整数のみがメモリにロードされ、エンティティ オブジェクトはロードされません。

これで、次のことができます。

importedEntities.Where(x => newIds.Contains(x.ID))

長すぎないnewIds限り。長すぎるとは?linq ステートメントは、IN簡単に数千の項目を含むことができる句を生成しますが、たとえば 10,000 よりも長い場合は、おそらく ID をチャンクで処理する必要があります。

ところで(1)。ここでは、両方のリストが異なるコンテキストにあり、おそらく異なるデータベースにあると想定しています。ただし、それらが同じコンテキストにある場合は、次の方法で成功する可能性があります。

importedEntities.Where(x => !currentEntities.Select(y => y.ID)
                           .Any(id => id == x.ID))

これにより、NOT EXISTSSQL クエリが生成されます。OutOfMemoryException「多くの」新しいアイテムがある場合でも、まだ遭遇する可能性があります。その場合、ページング メカニズム ( Skip- Take) を使用して、新しいアイテムをチャンクで処理できます。

ところで(2)、currentEntitiesとimportedEntitiesを入れ替えましたが、新しくインポートされたアイテムに興味があると思うので、間違っていたら元に戻してください。

于 2012-08-21T19:02:40.583 に答える
0

これを試して。

以下は、importedEntities がデータベース内のコレクションではなく、メモリ内コレクションである場合です。

var importedEntityIds = importedEntities
    .Select(x => x.ID).ToList(); // Convert to a list of some value type.
var result = currentEntities
    .Where(x => !importedEntityIds.Contains(x.ID))
    .ToList();

以下は、importedEntities がデータベースのコレクションである場合です。

var importedEntityIds = importedEntities
    .Select(x => x.ID); // This will be translated in SQL to a NOT IN (...)
var result = currentEntities
    .Where(x => !importedEntityIds.Contains(x.ID))
    .ToList();
于 2012-08-21T05:41:12.203 に答える
0

システムなどに追加する必要がある新しいエントリを見つけようとしていると思います。これは珍しいことではありません。データセットが非常に大きいため、両方が同時にメモリ内に存在できないか、さまざまな LINQ 拡張メソッドがメモリ内にコレクションのコピーを作成し、メモリ内にリストのコピーが 3 つまたは 4 つあるため、最大メモリを使い果たします。

結果が IEnumerable になるという事実を使用できる場合があります。これは、SQL サーバーからの結果を反復処理するときにレコードのチャンクのみをロードします。

これを試して

foreach (var current in currentEntities)
{
    if (importedEntities.Contains(current))
    {
        importedEntities.Remove(current);
    }
}

このコードは、Equals および GetHashCode メソッドがオーバーライドされて、ポインターまたはいくつかの低レベルのものを比較するだけのオブジェクト基本クラスからのデフォルトのものではなく、プロパティによってオブジェクトを比較することを前提としています。

注:次のことを行う場合

foreach (var current in currentEntities.ToList())
{
    if (importedEntities.Contains(current))
    {
        importedEntities.Remove(current);
    }
}

比較しようとしている行だけでなく、データベース テーブル全体がメモリ内にあるため、System.OutOfMemoryException が発生します。したがって、ToList などを実行しないでください。

于 2012-08-21T13:17:56.587 に答える
-1

インポートされたエンティティからすべての ID を選択します。

var importedids = importedEntities.Select(entity => entity.ID).ToList();

ハッシュセットを作成します。

var importedidshashset = new HashSet<int>(importedids);

現在のエンティティをフィルター処理:

currentEntities.Where(entity => !importedidshashset.Contains(entity.ID)).ToList().

于 2012-08-21T06:09:15.143 に答える