簡體   English   中英

如何用表達式樹寫這個嵌套查詢?

[英]How Do I Write This Nested Query With Expression Trees?

動機

我有一個LINQ-to-SQL查詢,我需要為涉及泛型和動態類型的生產代碼編寫。 在SO的其他人的幫助下,我已經能夠確定我需要使用表達式樹創建查詢。 為了熟悉表達式樹,我首先嘗試編寫一個更簡單的靜態類型版本,但是我在查找嵌套查詢部分時遇到了麻煩。

我一直在使用MSDN作為參考,主要是本文

類型

這是將在示例查詢中使用的類型。 它是一個類的簡化版本,用於從SQL數據庫創建具有動態列數的網格。

public class PivotElement {
    public int Key { get; set; }
    public string DataField { get; set; }
    public string ColumnText { get; set; }
}

結果查詢

這是我想最終構建的查詢,用擴展語法編寫:

IQueryable<PivotElement> iQ = ...;
var copy = iQ;

// filterColumn and filterValue are strings that were passed in

copy = copy.Where( 
    pe1 => theIQ.Where( 
        pe2 => 
            pe1.Key == pe1.Key && 
            pe2.DataField == filterColumn && 
            pgr2.ColumnText.Contains( filterValue )
    ).Any()
);

此參數在元素旋轉之前過濾掉在請求的列中不包含請求的文本的行。

至今

這是我到目前為止所得到的。 我認為這是最正確的,但我不確定如何表明內部Where應始終在theIQtheIQ

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace IQueryableWhereTypeChange {
    class Program {
        static void Main( string[] args ) {
            var list = new List<PivotElement>() {
                new PivotElement() {
                    Key = 1, DataField = "FirstName", ColumnText = "Bob"
                },
                new PivotElement() {
                    Key = 1, DataField = "LastName", ColumnText = "Sanders"
                },
                new PivotElement() {
                    Key = 2, DataField = "FirstName", ColumnText = "Bob"
                },
                new PivotElement() {
                    Key = 2, DataField = "LastName", ColumnText = "Smith"
                },
                new PivotElement() {
                    Key = 3, DataField = "FirstName", ColumnText = "John"
                },
                new PivotElement() {
                    Key = 3, DataField = "LastName", ColumnText = "Smith"
                }
            };


            var theIQ = list.AsQueryable();
            var iQCopy = theIQ;

            var elementType = typeof( PivotElement );
            var delegateType = typeof( Func<PivotElement, bool> );

            var filterColumn = "LastName";
            var filterValue = "Smith";

            // Query is
            // iQCopy = iQCopy.Where( 
            //    pe1 => iQ.Where( 
            //        pe2 => 
            //            pe1.Key == pe1.Key && 
            //            pe2.DataField == filterColumn && 
            //            pgr2.ColumnText.Contains( filterValue )
            //    ).Any()
            //);

            // So all the elements for keys 2 and 3 should be in the
            //  result set, as those keys have a last name of Smith

            // build pe1
            Type elementType = typeof( PivotElement );
            ParameterExpression pe1 = Expression.Parameter( elementType, "pe1" );
            // build pe2
            ParameterExpression pe2 = Expression.Parameter( elementType, "pe2" );

            // pe1.Key
            Expression pe1KeyProp = Expression.Property( 
                pe1, elementType.GetProperty( "Key" )
            );
            // pe2.Key
            Expression pe2KeyProp = Expression.Property( 
                pe2, elementType.GetProperty( "Key" )
            );
            // build pe1.Key == pe2.Key
            Expression keyEquals = Expression.Equal( pe1KeyProp, pe2KeyProp );

            // build pe2.Datafield
            Expression pe2Datafield = Expression.Property( 
                pe2, elementType.GetProperty( "DataField" )
            );
            // build pe2.DataField == filterColumn
            Expression columnExpression = Expression.Constant( filterColumn );
            Expression dataEquals = Expression.Equal( 
                pe2Datafield, columnExpression 
            );

            // build pe2.ColumnText
            Expression pe2ColumnText = Expression.Property( 
                pe2, elementType.GetProperty( "ColumnText" )
            );
            // build pe2.ColumnText.Contains( filterValue )
            Type stringType = typeof(string);
            Expression valueExpression = Expression.Constant( filterValue );
            Expression textContains = Expression.Call( 
                pe2ColumnText, 
                stringType.GetMethod(
                    "Contains", 
                    new Type[] { stringType } ), 
                    new Expression[] { valueExpression }
            );

            // build pe1.Key == pe2.Key &&
            //       pe2.DataField == filterColumn && 
            //       pe2.ColumnText.Contains( filterValue )
            Expression innerCondition = Expression.AndAlso(
                keyEquals, Expression.AndAlso( dataEquals, textContains )
            );

            // build theIQ.Where( pe2 => innerCondition )
            // build theIQ.Where( pe2 => innerCondition ).Any()
            // build iQCopy.Where( pe1 => anyCall )
            // create the final query

            // enumerate results
            foreach( var pe in results ) {
                Console.WriteLine( 
                    "Key: " + pe.Key + 
                    ", DataField: " + pe.DataField + 
                    ", ColumnText: " + pe.ColumnText 
                );
            }
        }
    }

    public class PivotElement {
        public int Key { get; set; }
        public string DataField { get; set; }
        public string ColumnText { get; set; }
    }
}

丟失的一點擊中我,因為我得到了所有格式化的問題。 因為我已經把它全部輸了,所以我繼續發布它。 如果有人有興趣,這里有缺失的位:

// build theIQ.Where( pe2 => innerCondition )
Type queryableType = typeof( Queryable );
var delegateType = typeof( Func<PivotElement, bool> );
MethodCallExpression innerWhere = Expression.Call( 
    queryableType, 
    "Where", 
    new Type[] { elementType }, 
    new Expression[] { 
        theIQ.Expression, 
        Expression.Lambda(
            delegateType, innerCondition, new ParameterExpression[] { pe2 } 
        )
    } 
);

// build theIQ.Where( pe2 => innerWhere ).Any()
MethodCallExpression anyCall = Expression.Call( 
    queryableType, "Any", new Type[] { elementType }, innerWhere 
);

// build iQCopy.Where( pe1 => anyCall )
MethodCallExpression outerWhere = Expression.Call( 
    queryableType, 
    "Where",
    new Type[] { elementType }, 
    new Expression[] { 
        iQCopy.Expression, 
        Expression.Lambda( 
            delegateType, anyCall, new ParameterExpression[] { pe1 } 
        )
    }
);

// create the final query
var results = iQCopy.Provider.CreateQuery<PivotElement>( outerWhere );

theIQ.Expression告訴它使用來自theIQ查詢結構, iQCopy.Expression告訴它使用來自iQCopy查詢結構,最終的iQCopy.Provider告訴它將構造的查詢應用於實際的iQCopy實例。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM