web-dev-qa-db-ja.com

IEqualityComparerを実装して個別の値を返す方法は?

重複オブジェクトを含むデータを返すL2Eクエリがあります。これらの重複オブジェクトを削除する必要があります。基本的に、IDが同じ場合、オブジェクトは重複していると想定する必要があります。 q.Distinct()を試しましたが、それでも重複オブジェクトが返されました。次に、独自のIEqualityComparerを実装してDistinct()メソッドに渡しました。メソッドは次のテキストで失敗しました:

LINQ to Entitiesは、メソッド 'System.Linq.IQueryable1[DAL.MyDOClass] Distinct[MyDOClass](System.Linq.IQueryable1 [DAL.MyDOClass]、System.Collections.Generic.IEqualityComparer`1 [DAL.MyDOClass])'メソッドを認識せず、このメソッドを式を保存します。

EqualityComparerの実装は次のとおりです。

  internal class MyDOClassComparer: EqualityComparer<MyDOClass>
    {
        public override bool Equals(MyDOClass x, MyDOClass y)
        {
            return x.Id == y.Id;
        }

        public override int GetHashCode(MyDOClass obj)
        {
            return obj == null ? 0 : obj.Id;
        }
    }

それでは、自分のIEqualityComparerを適切に書くにはどうすればいいですか?

43

EqualityComparerを使用する方法ではありません。メモリ内の結果セットのみをフィルタリングできます。例:

var objects = yourResults.ToEnumerable().Distinct(yourEqualityComparer);

GroupByメソッドを使用してIDでグループ化し、Firstメソッドを使用して、データベースがIDごとに一意のエントリのみを取得できるようにします。例:

var objects = yourResults.GroupBy(o => o.Id).Select(g => g.First());
116
Rich O'Kelly

rich.okellyとLadislav Mrnkaは、どちらも異なる意味で正しいです。

両方の答えは、_IEqualityComparer<T>_のメソッドがSQLに変換されないという事実に対処しています。

それぞれの長所と短所を見る価値があると思いますが、コメントよりも少し時間がかかります。

richのアプローチは、クエリを別のクエリに書き換え、同じ究極の結果をもたらします。それらのコードは、手作業でコーディングされたSQLを使用して効率的にこれを行う方法を多かれ少なかれ必要とします。

Ladislav'sは、個別の前の時点でデータベースからそれを引き出し、その後、インメモリアプローチが機能します。

データベースは、リッチグループが依存する種類のグループ化とフィルタリングを行うのに優れているため、この場合はおそらく最もパフォーマンスが高くなります。ただし、このグループ化の前に行われていることの複雑さは、Linq-to-entitiesが単一のクエリを適切に生成せず、むしろ多数のクエリを生成し、メモリ内の作業の一部を実行することであることがわかりますかなり厄介かもしれません。

一般に、グループ化はメモリ内の場合に区別するよりも高価です(特に、AsList()ではなくAsEnumerable()でメモリに取り込む場合)。そのため、他の要件のためにこの段階で既にメモリにそれを持ち込んでいる場合は、パフォーマンスが向上します。

また、同等性の定義がデータベースで使用可能なものとあまり関係のない場合は唯一の選択肢であり、もちろん、_IEqualityComparer<T>_はパラメーターとして渡されます。

総じて、リッチはここでの最良の選択である可能性が最も高いと思う答えですが、リッチと比較してラディスラフの長所と短所は、研究と検討の価値もあります。

16
Jon Hanna

あなたはしません。 Distinct演算子はデータベースで呼び出されるため、アプリケーションで記述したコードは使用できません(等値比較ロジックをSQLに移動することはできません)。あなたの申請。

var query = (from x in context.EntitySet where ...).ToList()
                                                   .Distinct(yourComparer);
7
Ladislav Mrnka

遅い答えですが、より良い方法があります:DALオブジェクトが部分的である場合(通常はDBオブジェクトである場合)、次のように拡張できます。

public partial class MyDOClass :  IEquatable<MyDOClass>
    {

        public override int GetHashCode()
        {
            return Id == 0 ? 0 : Id;
        }

        public bool Equals(MyDOClass other)
        {
            return this.Id == other.Id;
        }
    }

そして、このオーバーロードはオーバーロードなしで機能します。

そうでない場合は、次のようにIEqualityComparerクラスを作成できます。

internal class MyDOClassComparer : MyDOClass,  IEquatable<MyDOClass>, IEqualityComparer<MyDOClass>
    {
        public override int GetHashCode()
        {
            return Id == 0 ? 0 : Id;
        }

        public bool Equals(MyDOClass other)
        {
            return this.Id == other.Id;
        }

        public bool Equals(MyDOClass x, MyDOClass y)
        {
            return x.Id == y.Id;
        }

        public int GetHashCode(MyDOClass obj)
        {
            return Id == 0 ? 0 : Id;
        }
    }

繰り返しになりますが、過負荷なしでDistinctを使用してください

2
gil kr

GroupBy()Distinct()よりも良い解決策かもしれません-前述のように 最高評価の答えで on この質問

2
Martin Zaloga