3

アイテムのコレクション (ADO.NET Entity Framework) があり、いくつかの異なる基準に基づいて検索結果としてサブセットを返す必要があります。残念ながら、条件が重複しているため、返される必要がある有効なアイテムが除外または重複するためWhere、条件が満たされた (または条件が満たされていない) コレクションを取得することはできません。Where

各チェックを個別に行い、結果を組み合わせることにしました。を使用することを検討しAddRangeましたが、結果リストに重複が生じます (私の理解では、毎回コレクションが列挙されます - ここで正しい/間違っていますか?)。Union重複を挿入せず、必要になるまで列挙を延期することに気付きました(繰り返しますが、この理解は正しいですか?)。

検索は次のように記述します。

IEnumerable<MyClass> Results = Enumerable.Empty<MyClass>();
IEnumerable<MyClass> Potential = db.MyClasses.Where(x => x.Y); //Precondition

int parsed_key;

//For each searchable value
foreach(var selected in SelectedValues1)
{
    IEnumerable<MyClass> matched = Potential.Where(x => x.Value1 == selected);
    Results = Results.Union(matched); //This is where the problem is
}

//Ellipsed....

foreach(var selected in SelectedValuesN) //Happens to be integer
{
    if(!int.TryParse(selected, out parsed_id))
        continue;
    IEnumerable<MyClass> matched = Potential.Where(x => x.ValueN == parsed_id);
    Results = Results.Union(matched); //This is where the problem is
}

ただし、それResults = Results.Union(matched)は のように機能しているようResults = matchedです。いくつかのテスト データとテスト検索を実行しました。検索では、最初のフィールドが -1、0、1、または 3 の結果が求められます。これにより、4 つの結果 (2 つの 0、1 および 3) が返されます。ループの最初の反復は期待どおりに機能し、Results はまだ空のままです。2 番目の反復も期待どおりに機能し、Results には 2 つの項目が含まれます。ただし、3 回目の繰り返しの後、Results には項目が 1 つだけ含まれます。

仕組みを誤解しただけ.Unionですか、それともここで何か他のことが起こっていますか?

4

5 に答える 5

8

遅延実行のため、最終的に を消費するまでに、 すべてが の最後の値に基づいている多くのクエリResultsの結合になります。Where selected

だからあなたは持っています

Results = Potential.Where(selected)
    .Union(Potential.Where(selected))
    .Union(potential.Where(selected))...

そしてすべてのselected値は同じです。

var currentSelected = selectedループ内に を作成し、それをクエリに渡す必要があります。そうすれば、の各値がselected個別に取得され、この問題は発生しません。

于 2012-08-13T14:08:14.440 に答える
1

これをもっと簡単に行うことができます:

Reuslts = SelectedValues.SelectMany(s => Potential.Where(x => x.Value == s));

(これは重複を返す可能性があります)

または

Results = Potential.Where(x => SelectedValues.Contains(x.Value));
于 2012-08-13T14:05:51.260 に答える
1

他の人が指摘したように、あなたのLINQ式はクロージャーです。これは、変数selectedが foreach ループの各反復で LINQ 式によってキャプチャされることを意味します。foreach の各反復で同じ変数が使用されるため、最終的には最後の値が何であれになります。これを回避するには、次のように foreach ループ内でローカル変数を宣言する必要があります。

//For each searchable value 
foreach(var selected in SelectedValues1) 
{
    var localSelected = selected;
    Results = Results.Union(Potential.Where(x => x.Value1 == localSelected));
}

使用するだけではるかに短くなります.Contains()

Results = Results.Union(Potential.Where(x => SelectedValues1.Contains(x.Value1)));

複数のコレクションを照会する必要があるためSelectedValues、オブジェクトの正しいフィールド/プロパティを一致させる方法が必要ですが、それらをすべて独自のコレクション内に配置して、それを反復処理することもできます。

フィールド/プロパティの名前をキーとして、選択した値のリストをディクショナリに保存することで、これを行うことができます。Reflection を使用して正しいフィールドを検索し、チェックを実行します。次に、コードを次のように短縮できます。

// Store each of your searchable lists here
Dictionary<string, IEnumerable<MyClass>> DictionaryOfSelectedValues = ...;

Type t = typeof(MyType);
// For each list of searchable values
foreach(var selectedValues in DictionaryOfSelectedValues) // Returns KeyValuePair<TKey, TValue>
{
    // Try to get a property for this key
    PropertyInfo prop = t.GetProperty(selectedValues.Key);
    IEnumerable<MyClass> localSelected = selectedValues.Value;

    if( prop != null )
    {
        Results = Results.Union(Potential.Where(x =>
                localSelected.Contains(prop.GetValue(x, null))));
    }
    else // If it's not a property, check if the entry is for a field
    {
        FieldInfo field = t.GetField(selectedValues.Key);
        if( field != null )
        {
            Results = Results.Union(Potential.Where(x =>
                    localSelected.Contains(field.GetValue(x, null))));
        }
    }
}
于 2012-08-13T14:39:47.240 に答える
0

Unionさて、遅延実行を使用しているため、問題が発生していると思います。

するとどうなるか、

var unionResults = Results.Union(matched).ToList();
Results = unionResults; 
于 2012-08-13T14:11:22.803 に答える
0

いいえ、ユニオンの使用は絶対に正しいです。覚えておくべき唯一のことは、等値演算子に基づいて重複を除外することです。サンプルデータはありますか?

于 2012-08-13T14:06:04.040 に答える