![](/img/trans.png)
[英]How to declare a Linq Expression variable in order to have it processed as a dbParameter
[英]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.