どのように私は2つのメンバーの式ツリーを組み合わせていますか?
-
05-09-2019 - |
質問
私は、単一の式に次の式を結合しようとしています。私は、キーセレクタに別途項目セレクタを取るのOrderByメソッドを作成することができますので、これを実行する必要があります。これは、[並べ替えのオーバーロードのいずれかを使用してIComparer<T>
を提供し、達成することができますが、それはSQLに変換しません。
に続いて、さらに私は動作しませんが、ポイントを説明する必要があり、実装に伴い、achiveしようとしているものを明確にするメソッドのシグネチャです。
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メソッドをしたい理由は、彼らがさまざまな方法で公開されているものの、多くの企業に適用され、複雑なキー選択式をサポートすることです。また、私は深いプロパティの文字列表現を使用してこれを行う方法を知ってんだけど、私はそれが強く型付けされた維持しようとしています。
解決
ので、普段遊びに部分式を持って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]
他のヒント
あなたがそこに持っていることは、突出して再度並べ替えた後、sotringされます。
.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
私は同じように作られたこの小さな拡張メソッドを必要としてます:
/// <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;
}