簡體   English   中英

使用表達式樹構建動態選擇

[英]Build Dynamic Select using Expression Trees

如何使用表達式樹生成以下內容...

var people = context.Set<Person>();
var transactions = context.Set<FinancialTransaction>();

var dataview = people.Where( p => p.LastName == "Smith" );

var selection = dataview
        .Select( p => new
        {
            FirstName = p.FirstName,
            LastName = p.LastName,
            LastTransaction =
                transactions
                    .Where( t => t.AuthorizedPersonId == p.Id )
                    .Max( t => t.TransactionDateTime )
        } );

gReport.AutoGenerateColumns = true;
gReport.DataSource = selection.ToList();
gReport.DataBind();

我正在嘗試使用Ethan Brown 在這里提供的LinqRuntimeTypeBuilder解決方案,但在如何為LastTransaction子查詢創建表達式以及如何將查詢綁定到GridView方面苦苦掙扎。

這就是我到目前為止......

var people = context.Set<Person>();
var transactions = context.Set<FinancialTransaction>();

var dataview = people.Where( p => p.LastName == "Smith" );

var dynamicFields = new Dictionary<string, Type>();
dynamicFields.Add( "FirstName", typeof( string ) );
dynamicFields.Add( "LastName", typeof( string ) );
dynamicFields.Add( "LastTransaction", typeof( DateTime? ) );

Type dynamicType = Rock.Data.LinqRuntimeTypeBuilder.GetDynamicType( dynamicFields );

ParameterExpression sourceItem = Expression.Parameter( dataview.ElementType, "x" );

// Is this right? if if so how do I bind it to the dynamic field????
Expression<Func<Person, DateTime>> lastTransactionSelect = a => transactions.Where( t => t.AuthorizedPersonId == a.Id && t.TransactionDateTime.HasValue ).Max( t => t.TransactionDateTime.Value );

var bindings = new List<MemberBinding>();
bindings.Add( Expression.Bind( dynamicType.GetField( "FirstName" ), Expression.Property( sourceItem, dataview.ElementType.GetProperty( "FirstName" ) ) ) );
bindings.Add( Expression.Bind( dynamicType.GetField( "LastName" ), Expression.Property( sourceItem, dataview.ElementType.GetProperty( "LastName" ) ) ) );
bindings.Add( Expression.Bind( dynamicType.GetField( "LastTransaction" ), ??? ) );

Expression selector = Expression.Lambda( Expression.MemberInit( Expression.New( dynamicType.GetConstructor( Type.EmptyTypes ) ), bindings ), sourceItem );

var query = dataview.Provider.CreateQuery(
    Expression.Call(
        typeof( Queryable ),
        "Select",
        new Type[] { dataview.ElementType, dynamicType },
    Expression.Constant( dataview ), selector ) ).AsNoTracking();

// Can't bind directly to the query since it's a DBQuery object
gReport.DataSource = ???;

gReport.DataBind();

如何為子查詢創建表達式,然后將查詢綁定到GridView的最佳方法是什么?

在使用Reflector評估編譯器如何生成linq語句之后,這里是我最終為子選擇創建表達式的方法......

ParameterExpression transactionParameter = Expression.Parameter(typeof(FinancialTransaction), "t");
MemberExpression authorizedPersonIdProperty = Expression.Property(transactionParameter, "AuthorizedPersonId");
MemberExpression transactionDateTime = Expression.Property(transactionParameter,"TransactionDateTime");

MethodInfo whereMethod = GetWhereMethod();
MethodInfo maxMethod = GetMaxMethod();

var personIdCompare = new Expression[] { 
    Expression.Constant(transactions), 
    Expression.Lambda<Func<FinancialTransaction, bool>>( Expression.Equal(authorizedPersonIdProperty, Expression.Convert(idProperty, typeof(int?))), new ParameterExpression[] { transactionParameter } ) 
};
var transactionDate = Expression.Lambda<Func<FinancialTransaction, DateTime?>>( transactionDateTime, new ParameterExpression[] { transactionParameter } );
var lastTransactionDate = Expression.Call( null, maxMethod, new Expression[] { Expression.Call( null, whereMethod, personIdCompare ), transactionDate } );

...

bindings.Add( Expression.Bind( dynamicType.GetField( "LastTransaction" ), lastTransactionDate ) );


...


private MethodInfo GetWhereMethod()
{
    Func<FinancialTransaction, bool> fake = element => default( bool );
    Expression<Func<IEnumerable<FinancialTransaction>, IEnumerable<FinancialTransaction>>> lamda = list => list.Where( fake );
    return ( lamda.Body as MethodCallExpression ).Method;
}

private MethodInfo GetMaxMethod()
{
    Func<FinancialTransaction, DateTime?> fake = element => default( DateTime? );
    Expression<Func<IEnumerable<FinancialTransaction>, DateTime?>> lamda = list => list.Max( fake );
    return ( lamda.Body as MethodCallExpression ).Method;
}

我知道您的主要問題是關於構建動態linq表達式樹,但我可能能夠幫助解決有關將Queryable綁定到網格的第二個問題。

編輯:對不起,我實際上嘗試了這個並且做OfType <object>生成了一個Casting異常,所以這里有一些實際可行的

var query = dataview.Provider.CreateQuery(
    Expression.Call(
        typeof( Queryable ),
        "Select",
        new Type[] { dataview.ElementType, dynamicType },
    Expression.Constant( dataview ), selector ) ).AsNoTracking();

// enumerate thru the query results and put into a list
var listResult = new List<object>();
var enumerator = query.GetEnumerator();
while ( enumerator.MoveNext() )
{
    reportResult.Add( enumerator.Current );
}

gReport.DataSource = listResult;

gReport.DataBind();

暫無
暫無

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

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