Filtrage de la collection avec LINQ

Disons que nous avons une collection d’objects Personne

class Person { public ssortingng PersonName {get;set;} public ssortingng PersonAddress {get;set;} } 

Et quelque part dans la collection définie par le code

 List pesonsList = new List(); 

Nous avons besoin d’un filtre qui doit filtrer la collection et renvoyer le résultat à l’utilisateur final. Disons que nous avons une collection d’objects de type filtre

 class Filter { public ssortingng FieldName {get;set;} public ssortingng FilterSsortingng {get;set;} } 

Et quelque part dans le code que nous avons

 List userFilters = new List(); 

Nous devons donc filtrer le contenu de la collection personnesList par les filtres définis dans la collection userFilters. Où le Filter.FieldName == “PersonName” || Filter.FieldName == “PersonAddress” . Comment puis-je faire cela avec LINQ de manière cool? Les solutions telles que switch / case, ou peuvent être, ai-je pensé, une méthode d’extension sur personsList qui détermine, à partir du FiledName, la propriété de la personne à examiner, sont connues. Autre chose ? Quelque chose de délicat 🙂 Merci.

Vous pouvez créer une expression lambda pour créer un prédicat approprié à l’aide de la classe Expression .

 public static Expression> CreateFilterExpression( IEnumerable filters) { ParameterExpression param = Expression.Parameter(typeof(TInput), ""); Expression lambdaBody = null; if (filters != null) { foreach (Filter filter in filters) { Expression compareExpression = Expression.Equal( Expression.Property(param, filter.FieldName), Expression.Constant(filter.FilterSsortingng)); if (lambdaBody == null) lambdaBody = compareExpression; else lambdaBody = Expression.Or(lambdaBody, compareExpression); } } if (lambdaBody == null) return Expression.Lambda>(Expression.Constant(false)); else return Expression.Lambda>(lambdaBody, param); } 

Avec cette méthode d’assistance, vous pouvez créer une méthode d’extension sur n’importe quelle classe IQueryable . Elle devrait donc fonctionner pour tous les backends LINQ:

 public static IQueryable Where(this IQueryable source, IEnumerable filters) { return Queryable.Where(source, CreateFilterExpression(filters)); } 

… que vous pouvez appeler comme ceci:

 var query = context.Persons.Where(userFilters); 

Si vous souhaitez également prendre en charge les collections IEnumerable , vous devez utiliser cette méthode d’extension supplémentaire:

 public static IEnumerable Where(this IEnumerable source, IEnumerable filters) { return Enumerable.Where(source, CreateFilterExpression(filters).Comstack()); } 

Notez que cela ne fonctionne que pour les propriétés de chaîne. Si vous souhaitez filtrer des champs, vous devez modifier Expression.Property en Expression.Field (ou MakeMemberAccess ), et si vous devez prendre en charge d’autres types que les propriétés de chaîne, vous devrez fournir davantage d’informations sur le type à Expression.Constant partie de la méthode CreateFilterExpression .

Vous pouvez le faire via la reflection:

 IQueryable filteredPersons = personsList.AsQueryable(); Type personType = typeof(Person); foreach(Filter filter in userFilters) { filteredPersons = filteredPersons.Where(p => (ssortingng)personType.InvokeMember(filter.FieldName, BindingFlags.GetProperty, null, p, null) == filter.FilterSsortingng); } 

(non compilé, mais cela devrait aller dans la bonne direction)

Tu ne peux pas juste faire

 personList.Where(x => x.PersonName == "YourNameHere").ToList() ? 

J’appendais une méthode à la classe Filter pour vérifier si le filtre est satisfait:

 class Filter { public ssortingng FieldName {get;set;} public ssortingng FilterSsortingng {get;set;} public bool IsSatisfied(object o) { return o.GetType().GetProperty(FieldName).GetValue(o, null) as ssortingng == FilterSsortingng; } 

Vous pouvez ensuite l’utiliser comme ceci:

 var filtered_list = personsList.Where(p => userFilters.Any(f => f.IsSatisfied(p)));