2011-04-20 2 views
2

Je suis en train de filtre d'abord un ensemble de produits, puis de les commanderentités de filtre, puis trier sur le champ dynamique

(repository.GetAllProducts() 
    .Where(x=>x.Naam.Contains("filter")) as ObjectQuery<Product>) 
    .FunkyOrder<Product>(sortfield,sortdirection) 

En utilisant la méthode d'extension:

public static IEnumerable<T> FunkyOrder<T>(this ObjectQuery<T> input, string fieldname = "Id", string sortdirection = "asc") 
     {    
      switch (sortdirection) 
      { 
       case "dsc": 
       case "desc": 
        return input.OrderBy("it." + fieldname + " DESC"); 
       default: 
        return input.OrderBy("it." + fieldname); 

      } 
     } 

(les GetAllProducts() de mon dépôt retourne une méthode IEnumerable

Cette FunkyOrder devrait fonctionner, mais que - comme ObjectQuery - retours cast NULL

Je ne peux utiliser que FunkyOrder avec ObjectQuery, car cela semble être la seule chose qui supporte les requêtes "it.FieldName".

Si je change la - comme - notation à Un casting régulier je reçois:

Unable to cast object of type 'WhereEnumerableIterator`1[MySecretNameSpace.Product]' to type 'System.Data.Objects.ObjectQuery`1[MySecretNameSpace.Product]'. 

Est-il possible de faire ce travail ou que je serai obligé de soit vivre avec le tri avant le filtrage ou écrire un interrupteur géant avec des expressions lambda pour mon tri?

Répondre

7

Ceci s'est avéré être un mal de tête GIANT à essayer de résoudre; mais à la fin, il en fait avéré être très facile à réaliser avec un peu de réflexion et hackishness:

public class Program 
{ 
    public class SomeClass 
    { 
     public int Id { get; set; } 
     public string Name { get; set; } 
    } 

    static void Main(string[] args) 
    { 
     List<SomeClass> sample = new List<SomeClass> 
     { 
      new SomeClass { Id = 4, Name = "ABC" }, 
      new SomeClass { Id = 1, Name = "XYZ" }, 
      new SomeClass { Id = 2, Name = "JKL" } 
     }; 

     var result = sample.OrderByDynamic("Name", OrderDirection.Ascending).ToList(); 

     result.ForEach(x => Console.WriteLine("Id: " + x.Id + " | Name: " + x.Name)); 

     Console.ReadKey(); 
    } 
} 

public enum OrderDirection 
{ 
    Ascending, 
    Descending 
} 

public static class LinqExtensions 
{ 
    public static IEnumerable<T> OrderByDynamic<T>(this IEnumerable<T> source, string propertyName, OrderDirection direction = OrderDirection.Ascending) 
    { 
     if(direction == OrderDirection.Ascending) 
      return source.OrderBy(x => x.GetType().GetProperty(propertyName).GetValue(x, null)); 
     else 
      return source.OrderByDescending(x => x.GetType().GetProperty(propertyName).GetValue(x, null)); 
    } 
} 

Cet exemple a fonctionné pour moi dans des situations de test; Je pourrais fournir le nom de la propriété, et spécifier une direction de tri, et cela a bien fonctionné. Il suffit d'appeler comme ça:

yourEnumerable.OrderByDynamic("YourPropertyName"); 
yourEnumerable.OrderByDynamic("YourPropertyName", OrderDirection.Descending); 

Enjoy!

+0

J'ai remplacé votre OrderDirection par SortDirection de System.Web.UI.WebControls; On dirait que votre mal de tête en valait la peine, ça fonctionne bien! –

+0

Vraiment impressionnant, merci @Tejs, merci. –

2

Je changer votre méthode FunkyOrder pour IEnumerable et mettre en œuvre votre tri dynamique en créant une expression dynamique comme si

public static IEnumerable<T> FunkyOrder<T, TResult>(this IEnumerable<T> input, string fieldname = "Id", string sortdirection = "asc") 
{ 
ParameterExpresssion parameter = Expression.Parameter(typeof(T), "p"); 
Expression property = Expression.PropertyOrField(parameter, fieldname); 

var lambda = Expression.Lambda<Func<T, TResult>>(property, parameter) 

if(sortdirection == "asc") 
{ 
    return input.OrderBy(lambda.Compile()); 
} 
else 
{ 
    return input.OrderByDescending(lambda.Complile()); 
} 
} 

Vous ne aurez plus besoin de jeter à ObjectQuery

+0

Cela ne fonctionne réellement pas. J'ai reproduit exactement ce code, et en essayant une expression comme 'x => x.Id', où le lambda renvoie Int32 se traduira par une conversion invalide à Func . Des conseils ici? – Tejs

+0

@Tejs J'ai mis à jour la réponse – Aducci

+0

Les arguments de type pour la méthode 'System.Linq.Enumerable.OrderByDescending (System.Collections.Generic.IEnumerable , System.Func )' ne peuvent pas être déduits de l'utilisation. Essayez de spécifier explicitement les arguments de type. –

1

Vous pouvez utiliser Dynamic Linq pour dynamique filtrage et tri. Cette petite bibliothèque (seulement un seul fichier source) contient des méthodes d'extension de IQueryable<T> comme:

public static IQueryable<T> Where<T>(this IQueryable<T> source, 
    string predicate, params object[] values) 

public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, 
    string ordering, params object[] values) 

//...and more like GroupBy, Select 

Votre requête sur IEnumerable pourrait alors être écrit:

repository.GetAllProducts().AsQueryable() 
    .Where(x => x.Naam.Contains("filter")) 
    .OrderBy("FieldName desc") 

La bibliothèque implémente tout le code de réflexion nécessaire pour effectuer ces requêtes dynamiques.

+0

Je ne veux pas vraiment utiliser linq dynamique juste pour cette tâche simple! Merci beaucoup quand même! –

Questions connexes