2016-12-27 4 views
0

Est-il possible de créer une expression de requête réutilisable avec des appels à OrderBy et Where, sans l'appliquer à un spécifique?Requête de méthode LINQ réutilisable

Je voudrais pouvoir faire quelque chose comme ceci:

var data = new List<PlayerDTO>(); 
var builder = new Builder<PlayerDTO>(); 
var query = builder.GetQuery(); 
var results = data.AsQueryable().Provider.CreateQuery(query); 

J'ai du mal à obtenir que cela fonctionne sans fournir l'objet IQueryable à la méthode GetQuery.

Ceci est le code que j'ai jusqu'à présent:

public Expression GetQuery() 
{ 
    var type = typeof(T); // T is PlayerDTO 
    var col = type.GetProperty(OrderBy.Column); 

    var orderByMethod = typeof(Queryable).GetMethods().Single(
     method => method.Name == "OrderBy" 
        && method.IsGenericMethodDefinition 
        && method.GetGenericArguments().Length == 2 
        && method.GetParameters().Length == 2); 

    var genericOrdebyMethod = orderByMethod.MakeGenericMethod(typeof(T), col.PropertyType); 

    var parameter = Expression.Parameter(typeof(T), "p"); // {p} 
    var property = Expression.Property(parameter, col); // {p.ID} 
    var lambda = Expression.Lambda<Func<T, int>>(property, parameter); // {p => p.ID} 

    //This list should not exist here 
    var tempList = new List<PlayerDTO>() { new PlayerDTO(1, "First"), new PlayerDTO(2, "Second") }; 

    var orderByMethodExpression = Expression.Call(genericOrdebyMethod, tempList.AsQueryable().Expression, lambda); // {tempList.OrderBy(p => p.ID)} 

    var results = tempList.AsQueryable().Provider.CreateQuery(orderByMethodExpression); 

    return orderByMethodExpression; 
} 

La partie pertinente est l'appel à Expression.Call, où je devais fournir une IQueryable il donc travailler, mais je voudrais être en mesure de construire l'expression sans spécifier un IQueryable existant.

Est-ce encore possible?

Edit:
Maintenant que je pense à cela, je ne ont pas besoin réellement faire du tout ... Il est parfaitement logique de simplement envoyer le IQueryable en tant que paramètre à la méthode GetQuery. Je vais garder cette question si.

+0

ne pouvez-vous utiliser seulement 'Expression > = p => p.ColumnName;'? Vous pouvez utiliser cette expression dans votre méthode 'OrderBy'. Cela fonctionnerait si votre PlayerDTO implémente une interface pour connaître la propriété columnName. Sinon, vous pouvez toujours utiliser la réflexion juste pour la propriété. –

+0

Oui, je pourrais, mais mon problème principal est avec les méthodes de type OrderBy/Where/etc que je voudrais inclure dans l'expression, puis réutiliser sur plusieurs IQueryables. –

+0

C'est juste un peu bizarre ce que vous faites, construire dynamiquement une expression, en utilisant Provider pour le transformer en iQueryable, alors que vous savez exactement à l'avance quelle propriété vous utiliserez et quelle méthode. Le Linq extensionmethods sur 'IQueryable 'fait exactement la même chose que vous essayez de faire manuellement maintenant (voir [ce lien] (https://referencesource.microsoft.com/#System.Core/System/Linq/IQueryable.cs , 7a2005ae434e9d9d, références)). –

Répondre

1

Vous pouvez créer comme une autre expression de IQueryable à IOrderedQueryable, quelque chose comme:

public Expression getOrderByQuery<T>(PropertyInfo col) 
{ 
    var orderByMethod = typeof(Queryable).GetMethods().Single(
     method => method.Name == "OrderBy" 
        && method.IsGenericMethodDefinition 
        && method.GetGenericArguments().Length == 2 
        && method.GetParameters().Length == 2); 

    var genericOrdebyMethod = 
         orderByMethod.MakeGenericMethod(typeof(T), col.PropertyType); 

    var parameter = Expression.Parameter(typeof(T), "p"); // {p} 
    var property = Expression.Property(parameter, col); // {p.ID} 
    var lambda = Expression.Lambda<Func<T, int>>(property, parameter); // {p => p.ID} 

    var paramList = Expression.Parameter(typeof(IQueryable<T>)); 

    // {tempList.OrderBy(p => p.ID)}  
    var orderByMethodExpression = Expression.Call(genericOrdebyMethod, paramList, lambda); 

    return Expression.Lambda(orderByMethodExpression, paramList); 
} 

Je l'utilise de cette façon, et ma liste se commander:

var tempList = new List<PlayerDTO>() 
    { 
    new PlayerDTO(2, "First"), 
    new PlayerDTO(1, "Second") 
    }; 

var e = (Expression<Func<IQueryable<PlayerDTO>, IOrderedQueryable<PlayerDTO>>>) 
       getOrderByQuery<PlayerDTO>(typeof(PlayerDTO).GetProperty("Id")); 
e.Compile().Invoke(tempList.AsQueryable()).Dump(); 
+0

Merci Maksim, ça a marché! Je trouve que le casting et l'appel de Compile sont vraiment laids, y a-t-il un moyen de le rendre plus propre ou est-ce aussi simple que possible? –

+0

@ShahinDohan Non, malheureusement, je ne le sais pas, vous pouvez utiliser votre méthode, bur pour autant que je sache, quelque part devrait être cast et compiler. –

+0

J'ai décidé de ne pas le faire du tout, mais je garderai la question si quelqu'un d'autre le fait. Merci en tout cas! –