Question

Je suis en train de combiner les expressions suivantes en une seule expression: item => item.sub, sub => sub.key devenir item => item.sub.key. Je dois faire ce que je puisse créer une méthode OrderBy qui prend séparément le sélecteur de l'élément du sélecteur à clé. Ceci peut être accompli en utilisant l'une des surcharges sur OrderBy et en fournissant une IComparer<T>, mais il ne se traduira pas à SQL.

Voici une signature de méthode pour préciser davantage ce que je suis en train de achive, ainsi que d'une mise en œuvre qui ne fonctionne pas, mais devrait illustrer le point.

    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
                 ));
    } 

qui serait appelé:

.OrderBy(item => item.Sub, sub => sub.Key)

Est-ce possible? Y at-il une meilleure façon? La raison pour laquelle je veux une méthode OrderBy qui fonctionne de cette façon est de soutenir une expression de sélection clé complexe qui s'applique à de nombreuses entités, si elles sont exposées de différentes façons. De plus, je suis au courant d'une façon de le faire en utilisant des représentations de chaîne de propriétés profondes, mais je suis en train de le garder fortement typé.

Était-ce utile?

La solution

Puisque c'est LINQ to SQL, vous pouvez généralement utiliser Expression.Invoke pour apporter une sous-expression en jeu. Je vais voir si je peux trouver un exemple ( Mise à jour: fait ). Notez toutefois que EF ne supporte pas - vous auriez besoin de reconstruire l'expression à partir de zéro. J'ai un code pour ce faire, mais il est assez long ...

Le code d'expression (à l'aide Invoke) est assez simple:

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);

Voici un exemple d'utilisation sur Northwind:

using(var ctx = new MyDataContext()) {
    ctx.Log = Console.Out;
    var rows = ctx.Orders.OrderBy(order => order.Customer,
        customer => customer.CompanyName).Take(20).ToArray();
}

Avec TSQL (reformaté pour s'adapter):

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]

Autres conseils

Qu'est-ce que vous avez, il est sotring, suivie par la projection puis le tri à nouveau.

.OrderBy(x => x.Sub)
    .Select(x => x.Sub)
        .OrderBy(x => x.Key)

Votre méthode pourrait ressembler à ceci:

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)
}

sera exécuté par SQL mais comme vous avez sans doute remarqué que je devais changer le type de retour ici pour IOrderedQueryable . Pouvez-vous travailler autour de cela?

Je avais besoin de la même manière en ce petit méthode d'extension:

    /// <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;
    }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top