[英]How do I combine two Member Expression Trees?
我正在嘗試將以下表達式組合成單個表達式:item => item.sub,sub => sub.key成為item => item.sub.key。 我需要這樣做,所以我可以創建一個OrderBy方法,它將項目選擇器分別帶到鍵選擇器。 這可以使用OrderBy上的一個重載並提供IComparer<T>
,但它不會轉換為SQL。
下面是一個方法簽名,以進一步闡明我想要實現的目標,以及一個不起作用的實現,但應該說明這一點。
public static IOrderedQueryable<TEntity> OrderBy<TEntity, TSubEntity, TKey>(
this IQueryable<TEntity> source,
Expression<Func<TEntity, TSubEntity>> selectItem,
Expression<Func<TSubEntity, TKey>> selectKey)
where TEntity : class
where TSubEntity : class
{
var parameterItem = Expression.Parameter(typeof(TEntity), "item");
...
some magic
...
var selector = Expression.Lambda(magic, parameterItem);
return (IOrderedQueryable<TEntity>)source.Provider.CreateQuery(
Expression.Call(typeof(Queryable), "OrderBy", new Type[] { source.ElementType, selector.Body.Type },
source.Expression, selector
));
}
這將被稱為:
.OrderBy(item => item.Sub, sub => sub.Key)
這可能嗎? 有沒有更好的辦法? 我希望以這種方式工作的OrderBy方法的原因是支持適用於許多實體的復雜鍵選擇表達式,盡管它們以不同方式公開。 另外,我知道使用深度屬性的字符串表示來實現此方法,但我試圖保持強類型。
由於這是LINQ-to-SQL,因此通常可以使用Expression.Invoke
來使用子表達式。 我會看看能否拿出一個例子( 更新:完成 )。 但請注意,EF不支持此功能 - 您需要從頭開始重建表達式。 我有一些代碼可以做到這一點,但它很冗長......
表達式代碼(使用Invoke
)非常簡單:
var param = Expression.Parameter(typeof(TEntity), "item");
var item = Expression.Invoke(selectItem, param);
var key = Expression.Invoke(selectKey, item);
var lambda = Expression.Lambda<Func<TEntity, TKey>>(key, param);
return source.OrderBy(lambda);
以下是Northwind的示例用法:
using(var ctx = new MyDataContext()) {
ctx.Log = Console.Out;
var rows = ctx.Orders.OrderBy(order => order.Customer,
customer => customer.CompanyName).Take(20).ToArray();
}
使用TSQL(重新格式化為適合):
SELECT TOP (20) [t0].[OrderID], -- snip
FROM [dbo].[Orders] AS [t0]
LEFT OUTER JOIN [dbo].[Customers] AS [t1]
ON [t1].[CustomerID] = [t0].[CustomerID]
ORDER BY [t1].[CompanyName]
我需要相同的這樣的小擴展方法:
/// <summary>
/// From A.B.C and D.E.F makes A.B.C.D.E.F. D must be a member of C.
/// </summary>
/// <param name="memberExpression1"></param>
/// <param name="memberExpression2"></param>
/// <returns></returns>
public static MemberExpression JoinExpression(this Expression memberExpression1, MemberExpression memberExpression2)
{
var stack = new Stack<MemberInfo>();
Expression current = memberExpression2;
while (current.NodeType != ExpressionType.Parameter)
{
var memberAccess = current as MemberExpression;
if (memberAccess != null)
{
current = memberAccess.Expression;
stack.Push(memberAccess.Member);
}
else
{
throw new NotSupportedException();
}
}
Expression jointMemberExpression = memberExpression1;
foreach (var memberInfo in stack)
{
jointMemberExpression = Expression.MakeMemberAccess(jointMemberExpression, memberInfo);
}
return (MemberExpression) jointMemberExpression;
}
你有什么東西,然后投射,然后再次排序。
.OrderBy(x => x.Sub)
.Select(x => x.Sub)
.OrderBy(x => x.Key)
你的方法可能是這樣的:
public static IOrderedQueryable<TSubEntity> OrderBy<TEntity, TSubEntity, TKey>(
this IQueryable<TEntity> source,
Expression<Func<TEntity, TSubEntity>> selectItem,
Expression<Func<TSubEntity, TKey>> selectKey)
where TEntity : class
where TSubEntity : class
{
return (IOrderedQueryable<TSubEntity>)source.
OrderBy(selectItem).Select(selectItem).OrderBy(selectKey)
}
這將由SQL執行,但您可能已經注意到我必須將返回類型更改為IOrderedQueryable <TSubEntity>。 你可以解決這個問題嗎?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.