複数のソーステーブルを使用する場合の 'Where'のlinq式の再利用方法

次のようにしましょう:

public interface IOne {
    UInt64 Id { get; }
    Int16 Size { get; }
}

public interface ITwo {
    UInt64 OneId { get; }
    Int16 Color { get; }
}

ここで説明したように、linq式を再利用する方法は次のような記述:

public static Expression> MyWhereExpression( int value ){
    return (o) => (o.Size > value);
}

int v = 5;
IQueryable records = from one in s.Query()
                                        .Where(MyWhereExpression(v))
                           select one;

2つのテーブルで同じことをしたいとき、私は問題に遭遇します。

表現:

public static Expression> MyWhereExpression2(int color ) {
    return (one,two) => (one.Id == two.OneId) && (two.Color > color );
}

Linq 1:

int color = 100;
IQueryable records = from one in s.Query()
                           from two in s.Query()
                                        .Where(MyWhereExpression2(color))
                           select one;

これは .Where from の2番目のものに限定されているため動作しません。

Linq 2:

int color = 100;
IQueryable records = (from one in s.Query()
                            from two in s.Query()
                            select new { one, two })
                           .Where(MyWhereExpression2(color));

これにより、

Argument 2: cannot convert from 'Expression>' to 'System.Func'

私はAnonymousTypeに関するエラーメッセージを理解していますが、クエリを書く方法を理解することはできません。

式を書くのではなく、表現を使用する理由

where (one.Id == two.OneId) && (two.Color > color ) 

linqクエリで直接この式を複数のlinqクエリで再利用したいからです。

6

3 答え

There may be a more elegant solution that escapes me at the moment, but you could just use Tuple instead of the anonymous type:

static Expression, bool>> MyWhereExpression2(int color) {
    return t => (t.Item1.Id == t.Item2.OneId) && (t.Item2.Color > color);
}

int color = 100;
IQueryable records = (from one in s.Query()
                            from two in s.Query()
                            select Tuple.Create(one, two))
                            .Where(MyWhereExpression2(color))
                            .Select(t => t.Item1);

UPDATE: I probably answered too quickly above as that won't work with Linq to SQL since the call to Tuple.Create cannot be translated to SQL. To work with Linq to SQL, the only solution I see at the moment is to create a named type:

class Pair
{
    public IOne One { get; set; }
    public ITwo Two { get; set; }
}

static Expression> MyWhereExpression2(int color) {
    return p => (p.One.Id == p.Two.OneId) && (p.Two.Color > color);
}

int color = 100;
IQueryable records = (from one in s.Query()
                            from two in s.Query()
                            select new Pair { One = one, Two = two })
                            .Where(MyWhereExpression2(color))
                            .Select(p => p.One);
3
追加された
ニック、これはすばらしい答えです。残念ながら、イベントは、NHibernate(3.2.0)では動作しません。 linq to SQLプロバイダは例外をスローします。
追加された 著者 mayu,
Zip()はLINQ to SQLのどちらでもサポートされていないようです。
追加された 著者 Teudimundo,

まず第一に、同じフィールドセットを持つ2つのテーブルがあるのはなぜですか?理想的にはデータベース設計では、異なるテーブル内の同じフィールド名を避けるべきです。デザインは継承を使用する必要があります。共通のフィールドは基本クラスに移動し、EFでは階層ごとにテーブルを作成して問題を解決します。

階層ごとのテーブルを使用してモデルを作成すると、インターフェイスは必要なくなり、linqクエリは共有フィルタ式を使用できます。

あるタイプから別のタイプに式を複製する複雑なリフレクトベースのメソッドを記述しなければ、求めているものを達成する方法はありません。

1
追加された
私はどこに同じフィールドのセットを持つ2つのテーブルがあると私は言ったのですか?私はカウントクエリのために式を再利用し、その後データを引き出すクエリを再利用したい。また、どのようにインターフェイスがlinqクエリに影響を与えるのか分かりません。
追加された 著者 mayu,

自分の質問に答える...

いくつかの実験(Nick Guerreraの元の解答を含む)の後、私は別のアプローチを取った - 式を再利用しようとする代わりに、私はlinq全体を再利用する。しかし、コンテナ構造体を作成する必要がありました。

struct Pair {
    public IOne One { get; set; }
    public ITwo Two { get; set; }
    public Pair(IOne one, ITwo two) : this() {
        One = one;
        Two = two;
    }
}


public IQueryable Get(ISession s, int color) {
            return from one in s.Query()
                   from two in s.Query()
                   where (one.Id == two.OneId) && (two.Color > color)
                   select new Pair(one, two);
        }

今私は電話することができます

Get(s, color).Count();

そして

var records = (from data in Get(s, color) select data).Take(2000);

1
追加された