演算子によるラムダ式のグループ化とDapperExtensionsの述語グループでのそれらの使用

c# dapper extension-methods

質問

私の以前の質問に従う: Pulling Apart Expression<Func<T, object>> - 私はそれを少し高度化しようとしています。現在、私はこれを行うことができます:

var matchingPeople = Connection.Get<Person>(p => p.MarketId == marketId);

これはDapperExtensions FieldPredicate変換されFieldPredicate

// Assume I've successfully parsed p => p.MarketId == marketId into its constituent parts:
// left = p => p.MarketId, theOperator = Operator.Eq, right = marketId
Predicates.Field(left, theOperator, right);

私は今これを行うことができるようにしたい:

var matchingPeople = Connection.Get<Person>(p => p.MarketId == marketId && p.FirstName == "John" || p.FirstName == "Jack");

次のようなSQLを生成します。

DECLARE @MarketId INT = 3
DECLARE @FirstName01 VARCHAR(MAX) = 'John'
DECLARE @FirstName02 VARCHAR(MAX) = 'Jack'

SELECT *
FROM Person
WHERE MarketId = @MarketId AND (FirstName = @FirstName01 OR FirstName = @FirstName02)

DapperExtensions Compound Predicate Groupsを使用して:

// ** This is the code I am trying to dynamically create based on the lambda that is passed in **

var predicateGroupAnd = new PredicateGroup {Operator = GroupOperator.And, Predicates = new List<IPredicate>()};
// I already have the code to determine: left = p => p.MarketId, theOperator = Operator.Eq, right = marketId
predicateGroupAnd.Predicates.Add(Predicates.Field(left, Operator.Eq, right));

var predicateGroupOr = new PredicateGroup {Operator = GroupOperator.Or, Predicates = new List<IPredicate>()};
// I already have the code to determine: left = p => p.FirstName, theOperator = Operator.Eq, right = "John"
predicateGroupAnd.Predicates.Add(Predicates.Field(left, Operator.Eq, right));
// I already have the code to determine: left = p => p.FirstName, theOperator = Operator.Eq, right = "Jack"
predicateGroupOr.Predicates.Add(Predicates.Field(left, Operator.Eq, right));

var predicateGroupAll = new PredicateGroup // This is what will be passed to DapperExtensions' GetList<T> method
    {
        Operator = GroupOperator.And, // How do I set this correctly?
        Predicates = new List<IPredicate> {predicateGroupAnd, predicateGroupOr}
    };

私の問題は、表現木が解析される途中にあるようです。ラムダ式があるとします。

p => p.MarketId == marketId && p.FirstName == "John" || p.FirstName == "Jack"

これをBinaryExpressionキャストできます。 BinaryExpression.Leftを使用すると、

p.MarketId == marketId && p.FirstName == "John"

BinaryExpression.Rightは次のようにBinaryExpression.Rightます。

p.FirstName == "Jack"

また、 NodeType全体のBinaryExpressionは、ラムダの最後の条件演算子、つまりExpressionType.OrElse設定されているようです

私は、再帰を使用してラムダ式を右から左に移動する必要があるように感じますが、私が望む複合グループ述語を作成することはできませんでした。具体的には、 ANDラムダとORラムダをどのようにグループ化するのですか?ありがとう!

人気のある回答

あなたの例のラムダ、

p => p.MarketId == marketId && p.FirstName == "John" || p.FirstName == "Jack"

の等号

p => (p.MarketId == marketId && p.FirstName == "John") || p.FirstName == "Jack"

&&||よりも優先度が高いためです。

このため、あなたがツリーを取得します&&その後、「下」、(それが最初に計算する必要があるとして)で||上に:

                         ||
                        /  \
                       &&  Firstname == "Jack"
                      /  \
p.MarketId == marketId    p.FirstName == "John"

演算子の優先順位を理解すると、上記の意味が成り立ちます。あなたが代替を望むならば、ブラケットを使って||最初に評価される(それが式ツリーの最後に終わるようにする)。

p => p.MarketId == marketId && (p.FirstName == "John" || p.FirstName == "Jack")

あなたの一般的な問題は、あなたがこれに少し近づいているということです。現在、ORとANDの2つのグループを作成しようとしています。この場合、これはうまくいくかもしれませんが、一般的なものではありません。例えば、あなたはこれに対して何をしますか? (a && b) || (c && d)

私は、それぞれが、そしてそれぞれが、それ自身の述語グループに翻訳されるべきであると考えています。リンクされた記事の「複数の複合述語(述語グループ)」を参照してください。単にBinaryExpressionを述語グループに置き換えるだけです。

あなたの例では、(a && b)|| | cとのc頂点で。最終的に何をしたいのかは、式のリストとして左右の述語グループを作成する演算子があるたびにです。バイナリ表現を述語グループに変換するロジックでは、最初に同じ関数を使用して、左と右を述語グループに変換します

あなたのコードは||を見ます。
そのリストに式を追加する準備ができている述語グループを作成します
最初に左を選択します(問題ではありません)。
別のバイナリ表現が残っているので、ターゲットライブラリが理解できる述語群を得るためにそれ自身を呼び出します
再帰は&& bのみに作用します。最初に左を選択し、単純述語を表示します。これは、述語グループに単純に追加できることを意味します。
関数の元の呼び出しに戻ります。ここでは、左に式が下がっている述語グループがあり、別の述語グループに変換されて追加されています。それは単純に個々の述語であり、それをそのリストに追加することができます。

さて、もし私が狂ったようなものを持っていたら: a && b || c || d && e

さて、より高い優先順位を考慮すると、次のようになります: ((a && b) || c) || (d && e)括弧が最初の&&または最後のどちらかでcを入れることは100%確信していませんが、それは重要ではありませんが、論理は同じですツリーを視覚化するときは、角かっこ。それらは "葉"であり、外から働き、括弧を使って木を処理し、最終的にルートノードに到達します。 cの右側に:

       ||
     /    \
    ||     &&
   /  \   /  \
  &&   c d   e
 /  \
a    b


ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow