簡體   English   中英

在LinQ表達式樹中聲明變量

[英]Declare variable in LinQ expression tree

在上一個問題( Refine Enumerable LINQ WHERE語句帶有重復參數 )中,我通過使用let語句聲明變量來解決問題,我的代碼如下所示:

return
from q in pList
let currentContract = q.StaffContracts.OrderByDescending(p => p.SignedDate).Where(p => p.Active).FirstOrDefault()
where (currentContract.Timespan >= fromValue && currentContract.Timespan <= toValue)
select q;

在這種情況下, currentContract是重復的表達式。 現在我遇到了一個新問題。 我的方法不再提供或請求IQueryable ,但它需要一個Expression<Func<Staff, bool>>作為返回(意思是,只有WHERE子句)。

我試過這個但是沒有成功:

return q =>
{
    var currentContract = q.StaffContracts.OrderByDescending(p => p.SignedDate).Where(p => p.Active).FirstOrDefault();
    return currentContract != null && currentContract.Timespan >= fromValue && currentContract.Timespan <= toValue;
};

編譯錯誤消息是:

具有語句主體的lambda表達式無法轉換為表達式樹

這有什么解決方法嗎?

所以我們在這里可以做的是創建一個Compose方法,它接受一個Expression一個lambda的Expression ,一個參數和一個返回值,然后是第二個lambda,它接受第一個lambda的輸出作為它的輸入,然后返回一個新的lambda,它接受一個第一個參數的輸入並返回第二個參數的輸出。

如果它只是常規委托,那么該方法看起來就像這樣(只是為了讓你知道它在概念上做了什么):

public static Func<TFirst, TResult> Compose<TFirst, TIntermediate, TResult>(
    Func<TFirst, TIntermediate> first,
    Func<TIntermediate, TResult> second)
{
    return firstParam => second(first(firstParam));
}

這在Expression對象中實現,觸摸起來更復雜:

public static Expression<Func<TFirstParam, TResult>>
    Compose<TFirstParam, TIntermediate, TResult>(
    this Expression<Func<TFirstParam, TIntermediate>> first,
    Expression<Func<TIntermediate, TResult>> second)
{
    var param = Expression.Parameter(typeof(TFirstParam), "param");

    var newFirst = first.Body.Replace(first.Parameters[0], param);
    var newSecond = second.Body.Replace(second.Parameters[0], newFirst);

    return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}

這使用以下方法將一個Expression所有實例替換為另一個:

internal class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

public static Expression Replace(this Expression expression,
    Expression searchEx, Expression replaceEx)
{
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}

現在我們有了Compose方法,我們可以編寫一個表達式,它接受你的一個項目並返回一個Contract ,然后寫另一個方法來獲取該合同並計算一個bool指示它是否有效:

public static Expression<Func<Item, bool>> GetFilter(
    TimeSpan fromValue, TimeSpan toValue)
{
    Expression<Func<Item, Contract>> currentContract =
        q => q.StaffContracts
                .OrderByDescending(p => p.SignedDate)
                .Where(p => p.Active)
                .FirstOrDefault();

    return currentContract.Compose(contract =>
        contract != null &&
        contract.TimeSpan >= fromValue &&
        contract.TimeSpan <= toValue);
}

這里的Compose方法將在內部用currentContract的主體替換第二個lambda中的所有contract實例。 所以效果是如果你已經寫了三次,即使這可以防止你需要這樣做。

無論何時您想在表達式樹中創建變量(查詢提供程序不支持的內容),您都可以使用此Compose方法。 您始終可以創建一個計算變量值的方法,然后Compose該方法以在另一個表達式中使用它。

我擔心您必須使用System.Linq.Expressions.Expression類方法手動准備表達式:

public static Expression<Func<Item, bool>> GetWhere(TimeSpan fromValue, TimeSpan toValue)
{
    Expression<Func<Item, Contract>> currentContract =
        q => q.StaffContracts
              .OrderByDescending(p => p.SignedDate)
              .Where(p => p.Active)
              .FirstOrDefault();

    var param = currentContract.Parameters.First();

    return Expression.Lambda<Func<Item, bool>>(
                Expression.And(
                    Expression.And(
                        Expression.NotEqual(
                            currentContract,
                            Expression.Constant(null, typeof(Contract))),
                        Expression.GreaterThanOrEqual(
                            Expression.Property(currentContract, "Timespan"),
                            Expression.Constant(fromValue))),
                    Expression.LessThanOrEqual(
                        Expression.Property(currentContract, "Timespan"),
                        Expression.Constant(toValue))),
                param);
}

暫無
暫無

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

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