2009-08-24 19 views
9

मान लीजिए कि हमारे पास व्यक्तिगत ऑब्जेक्ट्स का संग्रह हैLINQ के साथ फ़िल्टरिंग संग्रह

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

और कहीं भी कोड परिभाषित संग्रह में

List<Person> pesonsList = new List<Person>(); 

हमें एक फ़िल्टर होना आवश्यक है जो संग्रह को फ़िल्टर करने और परिणाम को अंत में वापस करने की आवश्यकता है उपयोगकर्ता। आइए मान लें कि हमारे पास फ़िल्टर प्रकार ऑब्जेक्ट्स का संग्रह है

class Filter 
{ 
    public string FieldName {get;set;} 
    public string FilterString {get;set;} 
} 

और कोड में कहीं भी हमारे पास

List<Filter> userFilters = new List<Filter>(); 

है इसलिए हमें व्यक्तियों की सामग्री फ़िल्टर करने की आवश्यकता है उपयोगकर्ता संग्रह में परिभाषित फ़िल्टर द्वारा संग्रह संग्रह संग्रह संग्रह। जहां फ़िल्टर। फ़ील्डनाम == "पर्सननाम" || फ़िल्टर। फ़ील्डनाम == "पर्सन एड्रेस"। मैं LINQ के साथ एक शांत तरीके से ऐसा कैसे कर सकता हूं? स्विच/केस, या जैसे समाधान, मैंने सोचा था कि, व्यक्तियों पर विस्तार विधि, जो कि दर्ज की गई व्यक्ति की संपत्ति को दायर की गई है, को जाना जाता है। कुछ और ? कुछ मुश्किल :) धन्यवाद।

+0

क्या यह इन-मेमोरी LINQ या LinqToSql का उपयोग कर रहा है? – JustLoren

+0

यह इन-मेमोरी LINQ है। मुझे संग्रह में परिभाषित वस्तुओं के एक सेट को अन्य संग्रह में परिभाषित फ़िल्टर के साथ पूछने की आवश्यकता है। कोई डीबी बातचीत नहीं है। – Tigran

उत्तर

9

आप एक उचित विधेय Expression वर्ग का उपयोग कर बनाने के लिए एक लैम्ब्डा अभिव्यक्ति का निर्माण कर सकते ।

public static Expression<Func<TInput, bool>> CreateFilterExpression<TInput>(
                IEnumerable<Filter> 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.FilterString)); 
      if (lambdaBody == null) 
       lambdaBody = compareExpression; 
      else 
       lambdaBody = Expression.Or(lambdaBody, compareExpression); 
     } 
    } 
    if (lambdaBody == null) 
     return Expression.Lambda<Func<TInput, bool>>(Expression.Constant(false)); 
    else 
     return Expression.Lambda<Func<TInput, bool>>(lambdaBody, param); 
} 
इस सहायक विधि के साथ

, आप किसी भी IQueryable<T> वर्ग पर एक विस्तार विधि बना सकते हैं, तो यह हर LINQ बैकएंड लिए काम करना चाहिए:

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

... जो आप इस तरह कॉल कर सकते हैं:

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

आप के रूप में अच्छी IEnumerable<T> संग्रह का समर्थन करना चाहते हैं, तो आप इस अतिरिक्त विस्तार विधि का उपयोग करने की आवश्यकता होगी:

public static IEnumerable<T> Where<T>(this IEnumerable<T> source, 
              IEnumerable<Filter> filters) 
{ 
    return Enumerable.Where(source, CreateFilterExpression<T>(filters).Compile()); 
} 

ध्यान दें कि यह केवल स्ट्रिंग गुणों के लिए काम करता है। यदि आप फ़ील्ड पर फ़िल्टर करना चाहते हैं, तो आपको Expression.Property को Expression.Field (या MakeMemberAccess) में बदलना होगा, और यदि आपको स्ट्रिंग गुणों की तुलना में अन्य प्रकारों का समर्थन करने की आवश्यकता है, तो आपको CreateFilterExpression के Expression.Constant भाग में अधिक प्रकार की जानकारी प्रदान करनी होगी तरीका।

3

आप प्रतिबिंब के माध्यम से यह कर सकते हैं:

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

(संकलित नहीं है, लेकिन यह सही ट्रैक के साथ होना चाहिए)

+0

कूल! मैं नहीं जानता, ईमानदारी से, अगर मैं इस तकनीक को अपने असली कोड पर लागू करूँगा, वैसे ही यह बहुत अच्छा है। मुझे जेनेरिक सामान पसंद है, भले ही यह प्रदर्शन कारणों से इतना अच्छा न हो। दुर्भाग्यवश मैं आपके उत्तर के लिए मतदान करने में सक्षम नहीं हूं (मेरी प्रतिष्ठा 15 वर्ष से कम है), लेकिन उत्तर निश्चित रूप से महान है। – Tigran

+0

ध्यान दें कि यह अधिकांश LINQ बैकएंड पर काम नहीं करेगा; मुझे बहुत संदेह है कि एसक्यूएल के लिए लिंक एसक्यूएल में प्रतिबिंब कॉल का अनुवाद करने में सक्षम होगा। – Ruben

+0

हाँ, मैं सहमत हूं। वैसे, मेरे मामले में, मुझे किसी भी डीबी बातचीत की आवश्यकता नहीं है। – Tigran

0

मैं जाँच करने के लिए Filter वर्ग के लिए एक विधि जोड़ना होगा अगर फिल्टर संतुष्ट है:

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

तुम सिर्फ

नहीं कर सकते:

class Filter 
{ 
    public string FieldName {get;set;} 
    public string FilterString {get;set;} 

    public bool IsSatisfied(object o) 
    { return o.GetType().GetProperty(FieldName).GetValue(o, null) as string == FilterString; 
} 

फिर आप इसे इस तरह उपयोग कर सकते हैं

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

फ़िल्टर में फ़ील्डनाम संपत्ति शामिल है, इसलिए मुझे नहीं पता कि उपयोगकर्ता पर्सननाम या पर्सन एड्रेस के खिलाफ फ़िल्टर करना चाहता है या हो सकता है, या व्यक्ति वर्ग के कुछ अन्य संभावित गुण हो सकते हैं। – Tigran

संबंधित मुद्दे