LINQ-to-entities generic ==回避策

私は以下のLINQ-to-entitiesクエリを持っています

IQueryable> GetFirstOperationsForEveryId
    (IQueryable> ItemHistory)
{
    var q = (from h in ItemHistory
             where h.OperationId ==
                (from h1 in ItemHistory
                 where h1.GenericId == h.GenericId
                 select h1.OperationId).Min()
             select h);
    return q;
}

ItemHistory is a generic query. It can be obtained in the following way

var history1 = MyEntitiySet1.Select(obj =>
    new History{ obj.OperationId, GenericId = obj.LongId });
var history2 = AnotherEntitiySet.Select(obj =>
    new History{ obj.OperationId, GenericId = obj.StringId });

In the end of all I want a generic query being able to work with any entity collection convertible to History.

問題は、内部クエリのGenericId比較のためにコードがコンパイルされないということです(オペレータ '=='は 'T'および 'T'タイプのオペランドに適用できません)。

==を h1.GenericId.Equals(h.GenericId)に変更すると、次の NotSupportedException が表示されます。

タイプ 'System.Int64'をタイプして 'System.Object'にキャストすることができません。エンティティへのLINQは、エンティティデータモデルプリミティブ型のキャストのみをサポートします。

サブクエリの代わりにグループ化を行い、結果に参加しようとしました。

IQueryable> GetFirstOperationsForEveryId
    (IQueryable> ItemHistory)
{
    var grouped = (from h1 in ItemHistory
                   group h1 by h1.GenericId into tt
                   select new
                   {
                        GenericId = tt.Key,
                        OperationId = tt.Min(ttt => ttt.OperationId)
                   });

    var q = (from h in ItemHistory
             join g in grouped
                on new { h.OperationId, h.GenericId }
                equals new { g.OperationId, g.GenericId }
             select h);
    return q;
}

GenericIdは equals キーワードと比較され、動作しますが、実際のデータを持つクエリは遅すぎます(専用のpostgresqlサーバーで11時間実行されています)。

外側のwhereステートメントのための完全な式を構築するオプションがあります。しかし、このコードは長すぎて不明瞭です。

LINQ-to-entitiesのジェネリックとの等価比較の簡単な回避策はありますか?

2

4 答え

これを試してみてください。余分なクエリ/結合なしであなたが望むものを達成しなければならないと思います

IQueryable> GetFirstOperationsForEveryId
    (IQueryable> ItemHistory)
{
  var q = from h in ItemHistory
          group h by h.GenericId into tt
          let first = (from t in tt
                        orderby t.GenericId
                        select t).FirstOrDefault()
          select first;

  return q;
}
1
追加された
非常にエレガントなソリューション!しかし残念ながらNpgsqlではまだ実装されていません(2.0.11.92現在)。 System.Data.EntityCommandCompilationException:コマンド定義の準備中にエラーが発生しました。 ---> System.NotImplementedException:メソッドまたは操作が実装されていません。実際の問題は、インデックスを作成するのを忘れてしまったことです。そのため、グループ+結合クエリは11時間働いたのです。索引では、それはかなり速く実行されます。
追加された 著者 Mike,

また、GenericIdとOperationIdプロパティを実装するIItemHistoryインタフェースのTにジェネリック制約を設定することもできます。

0
追加された

私の質問にはすでに解決策が含まれています。 group + joinを使用する2番目の方法は、テーブルが適切にインデックス登録されている場合うまく動作します。データベース表から370k行を検索するのに3.28秒かかります。実際、ジェネリックでない変形では、最初のクエリはpostgresqlでは2番目のクエリより遅くなります。 26.68秒対4.75。

0
追加された
IQueryable> GetFirstOperationsForEveryId
(IQueryable> ItemHistory)
{
var grouped = (from h1 in ItemHistory
               group t by h1.GenericId into tt
               select new
               {
                    GenericId = tt.Key,
                    OperationId = tt.Min(ttt => ttt.OperationId)
               });

var q = (from h in ItemHistory
         join g in grouped
            on new { h.OperationId, h.GenericId }
            equals new { g.OperationId, g.GenericId }
         select h);
return q;
}
0
追加された
私が投稿したものとの違いは何ですか?
追加された 著者 Mike,