2016-02-11 16 views
6

muszę zapytania gromadzenia tak:LINQ ekspresji jako parametr

myList.Where(s => myFilters.Contains(s.CountryCode)) 

s.CountryCode powyżej stanowi jedynie przykład. Chcę, aby to zmienna i zadzwonić do różnych kolumn, tak:

myList.Where(s => myFilters.Contains(s.City)) 
myList.Where(s => myFilters.Contains(s.Region)) 
myList.Where(s => myFilters.Contains(s.Zipcode)) 

Więc chciałbym zdefiniować funkcję gdzie wyrażenie kolumna jest parametrem. Jaki jest podpis takiej funkcji?

public void MySelect(???) 
{ 
    myList.Where(s => myFilters.Contains(???); 
} 

myList jest ObservableCollection i myFilters jest List<string>

+0

Myślę, że powinieneś zdefiniuj typ zmiennej. –

+1

To zależy od typu danych 'myFilters'. Przedłuż swoje pytanie za pomocą tych informacji. –

+0

@VadimMartynov Nie rozumiem ... dlaczego musisz znać dane mojego filtra Myfilters Typ danych – Viru

Odpowiedz

10

Umieść tę metodę rozszerzenia w statycznej klasie:

public static IEnumerable<T> WhereContains<T, TValue> (this IEnumerable<T> obj, IEnumerable<TValue> container, Func<T, TValue> propertyAccess) 
{ 
    return obj.Where(o => container.Contains(propertyAccess(o))); 
} 

Droga ta dawny Metoda naprężenia działa, ponieważ akceptuje funkcję lambda, która rozwiązuje właściwość danego obiektu danego typu. Więc wystarczy przekazać mu prostą lambę, taką jak x => x.City.

Ponieważ jest całkowicie ogólny i nie jest specyficzny dla twojej kolekcji myFilters, musisz również przekazać go do funkcji. Ale to pozwala ci na używanie tego WhereContains również w wielu innych sytuacjach.

Korzystanie wygląda to tak:

// Test is a class with two string properties `City` and `Region` 
var myList = new List<Test>{ 
    new Test() { City = "asdf", Region = "fdsa" }, 
    new Test() { City = "foo", Region = "bar" }, 
    new Test() { City = "bar", Region = "baz" } 
}; 

var myFilters = new List<string> { "asdf", "bar" }; 

myList.WhereContains(myFilters, x => x.City); // contains asdf/fdsa and bar/baz 
myList.WhereContains(myFilters, x => x.Region); // contains foo/bar 
+1

To jest lepsze niż użycie refleksji ... +1 – Viru

+0

Kiedyś pomijałem ogólne metody myśląc o jego złożonej strukturze. dobry. – Manoj

1

Można użyć refleksji

public void MySelect(string column) 
{ 
    var result = myList.Where(s => myFilters.Contains(s.GetType().GetProperty(column))); 
} 
0

Jako alternatywne rozwiązanie można wykorzystać następujące podejście, które pozwala określić zarówno pole filtra lub funkcję filtra:

var adresses = new List<Address>{ 
    new Address() { City = "ABC", Country = "USA" }, 
    new Address() { City = "BC", Country = "USA" }, 
    new Address() { City = "C", Country = "UK" } 
}; 
var filterValues = new List<string> { "B", "UK", "U" }; 
// 
var FilterContains = [email protected]((values, value) => values.Contains(value)); 
var FilterStartsWith = [email protected]((values, value) => values.Any(v => value.StartsWith(v))); 
// 
var AdressesByCity = [email protected](a => a.City); 
var adressesByCitiesContains = AdressesByCity(filterValues, FilterContains); // B->{ABC;USA},{BC;USA} 
var adressesByCitiesStartsWith = AdressesByCity(filterValues, FilterStartsWith);// B->{BC;USA} 
// 
var AdressesByCountry = [email protected](a => a.Country); 
var adressesByCountriesContains = AdressesByCountry(filterValues, FilterContains);//U,UK-> {C;UK} 
var adressesByCountriesStartsWith = AdressesByCountry(filterValues, FilterStartsWith); //U,UK->{ABC;USA},{BC;USA}{C;UK} 

Tutaj metod przedłużających @Specify są realizowane w następujący sposób:

public static class @SpecifyExtension { 
    public static Func<IEnumerable<V>, Func<IEnumerable<V>, V, bool>, IEnumerable<U>> @Specify<U, V>(this IEnumerable<U> source, Func<U, V> selector) { 
     return (values, predicate) => source.Where(x => predicate(values, selector(x))); 
    } 
    public static Func<IEnumerable<TValue>, TValue, bool> @Specify<TValue>(this IEnumerable<TValue> source, Func<IEnumerable<TValue>, TValue, bool> func) { 
     return func; // for Type-inference only 
    } 
}