3

LINQ2SQL拡張メソッドのコレクションを書いています。現在、重複するメソッドがたくさんあります。1つはIEnumerableコレクションで動作し、もう1つはIQueryableコレクションで動作します。例えば

public static IQueryable<X> LimitVisible(this IQueryable<X> items) {
    return items.Where(x => !x.Hidden && !x.Parent.Hidden);
}

public static IEnumerable<X> LimitVisible(this IEnumerable<X> items) {
    return items.Where(x => !x.Hidden && !x.Parent.Hidden);
}

場合によってはIQueryableの動作を保持できるようにし、実行がデータベースに延期されるようにするため、この複製が必要だと思います。これは正しい仮定ですか、それとも単一のIEnumerableメソッド(および出力をIQueryableにキャストする)が機能しますか?両方が必要な場合は、複製が気に入らないので、一般的な方法を使用して2つを結合したいと思います。

public static T LimitVisible<T>(this T items) where T : IEnumerable<X> {
    return (T)items.Where(x => !x.Hidden && !x.Parent.Hidden);
}

これは、LimitVisible関数を記述したいX以外のタイプ(たとえばYとZ)があることを除いて機能します。でも書けない

public static T LimitVisible<T>(this T items) where T : IEnumerable<Y> {
    return (T)items.Where(y => !y.Hidden && !y.Parent.Hidden);
}

ジェネリックに基づいてオーバーロードできるからです。これらのメソッドを別のクラスに配置することもできますが、それは正しい解決策ではないようです。

助言がありますか?たぶん私は間違った仮定をしていて、そもそもIQueryable固有のバージョンは必要ありません。

編集:別のオプション

これは、重複を避けるために過去に使用した別のパターンです

private static readonly Expression<Func<X, bool>> F = x => !x.Hidden && !x.Parent.Hidden;

public static IEnumerable<X> LimitVisible(this IEnumerable<X> e) {
    return e.Select(W.Compile());
}

public static IQueryable<X> LimitVisible(this IQueryable<X> q) {
    return q.Select(W);
}

これには何か危険はありますか?

4

2 に答える 2

2

本当に2つの方法を持つことを回避する良い方法はありません。はパラメータとしてを取り、はパラメータIQueryable Whereとしてを取ります。ラムダの魔法のおかげで、コードは呼び出し元にとって同じように見えますが、コンパイラーは実際には2つの異なるコードビットで大きく異なることを行います。Expression<Func<T,bool>>IEnumerableFunc<T,bool>

于 2012-10-29T16:38:22.360 に答える
1

答えは使用法によって異なります。クエリを実行するためにデータセットをメモリに入れてもかまわない場合は、IEnumerable<T>オーバーライドのみを保持することで問題ありません。

ただし、この決定の必然的な結果は、を使用した瞬間にクエリがメモリに強制されることですLimitVisible<T>。これはあなたが望むものであるかもしれないし、そうでないかもしれません、そしてあなたがむしろ避けたいコーディングパターンにあなたを強制するかもしれません。例えば、

var firstHundred = ctx
    .SalesOrders
    .LimitVisible()                     // Happens in memory
    .Where(ord => ord.UserId == myUser) // Also happens in memory
    .Take(100);

サーバー側で状態をチェックするのではなく、すべての販売注文が可視性を判断する前に1つずつメモリに取得されるため、overideavailalbeIEnumerable<T>よりもシングルの方がパフォーマンスが大幅に低下します。ユーザーIDでフィルタリングすることでサーバー側の販売注文の大部分を排除できる場合、この同等のクエリのパフォーマンスの違いは桁違いに向上する可能性があります。IQueryable<T>

var firstHundred = ctx
    .SalesOrders
    .Where(ord => ord.UserId == myUser) // Happens in RDBMS
    .LimitVisible()                     // Happens in memory
    .Take(100);

自分だけでコードを使用することを計画していて、明らかな理由もなくパフォーマンスが悪い状況に注意することを気にしない場合は、1つの過負荷を維持できます。他の人にライブラリを使用させる予定がある場合は、両方のオーバーライドを保持することを強くお勧めします。

編集:代替実装を高速化するには、次のように式をプリコンパイルします。

private static readonly Expression<Func<X, bool>> F = x => !x.Hidden && !x.Parent.Hidden;
private static readonly Predicate<X> CompiledF = (Predicate<X>)F.Compile();

public static IEnumerable<X> LimitVisible(this IEnumerable<X> e) {
    return e.Select(CompiledF);
}

public static IQueryable<X> LimitVisible(this IQueryable<X> q) {
    return q.Select(F);
}
于 2012-10-29T16:39:40.633 に答える