2011-08-15 9 views
8

कहना चलो मैं निर्णय लेने से कुछ तार "मैच", इस तरह है कि क्या की एक विशेष तरीका है:LINQ से इकाइयों और सहायक तरीकों का उपयोग करते समय DRY कैसे रहें?

public bool stringsMatch(string searchFor, string searchIn) 
{ 
    if (string.IsNullOrEmpty(searchFor)) 
    { 
    return true; 
    } 

    return searchIn != null && 
    (searchIn.Trim().ToLower().StartsWith(searchFor.Trim().ToLower()) || 
    searchIn.Contains(" " + searchFor)); 
} 

मैं एक डेटाबेस संस्थाओं और इस सहायक को LINQ का उपयोग से बाहर मैचों को खींचने के लिए चाहते हैं। हालांकि, जब मैं कोशिश यह:

IQueryable<Blah> blahs = query.Where(b => stringsMatch(searchText, b.Name); 

मैं "संस्थाओं को LINQ विधि को नहीं पहचानता है ..."

के रूप में कोड अगर मैं फिर से लिखें:

IQueryable<Blah> blahs = query.Where(b => 
     string.IsNullOrEmpty(searchText) || 
     (b.Name != null && 
     (b.Name.Trim().ToLower().StartsWith(searchText.Trim().ToLower()) || 
     b.Name.Contains(" " + searchText))); 

कौन सा तर्कसंगत समकक्ष है, तो चीजें ठीक काम करती हैं। समस्या यह है कि कोड पठनीय नहीं है, और मुझे इसे अलग-अलग इकाई के लिए दोबारा लिखना है जिसे मैं मेल करना चाहता हूं।

जहां तक ​​मेरा बता सकते हैं this one जैसे प्रश्नों से, मुझे क्या करना चाहते हैं इस समय असंभव है, लेकिन मैं आशा करती हूं कि मैं कुछ याद कर रहा हूँ, मैं कर रहा हूँ?

+0

कोशिश करें [अनुमानक बिल्डर] (http://www.albahari.com/nutshell/predicatebuilder.aspx) – Eranga

उत्तर

4

LINQKit नामक एक स्वतंत्र रूप से उपलब्ध लाइब्रेरी का उपयोग करना (जैसा कि @ एरंगा द्वारा उल्लिखित है) यह कार्य उचित हो जाता है। कोड मैं अब LINQKit का उपयोग करते हुए दिखाई देता है:

protected Expression<Func<T, bool>> stringsMatch(string searchFor, Expression<Func<T, string>> searchIn) 
{ 
    if (string.IsNullOrEmpty(searchFor)) 
    { 
    return e => true; 
    } 

    return 
    e => 
    (searchIn.Invoke(e) != null && 
     (searchIn.Invoke(e).Trim().ToLower().StartsWith(searchFor.Trim().ToLower()) || 
     searchIn.Invoke(e).Contains(" " + searchFor))); 
} 

और (ध्यान दें AsExpandable() कॉल)

IQueryable<Blah> blahs = query().AsExpandable().Where(StringsMatch(searchText, b => b.Name)); 

जादू भागों searchin हैं इस तरह कहा जाता है की जरूरत है।(ई) कॉल और AsExpandable() का उपयोग करें जो एक रैपर परत जोड़ता है जो उन्हें काम करने की अनुमति देता है।

AsExpandable() बिट मूल लेखक here द्वारा विस्तार से समझाया गया है।

ध्यान दें कि मैं अभिव्यक्ति के कुछ विवरणों पर अभी भी थोड़ा सा आलसी हूं, इसलिए कृपया एक टिप्पणी जोड़ें/इसे उत्तर दें यदि इसे बेहतर/छोटा/स्पष्ट बनाया जा सके।

5

यदि आपके द्वारा फ़िल्टर किए जाने वाले सभी 'ब्लास' (वर्ग) में एक ही संरचना है, तो आप इस तरह की एक साधारण विधि का उपयोग कर सकते हैं। मुख्य अंतर यह है कि यह एक अभिव्यक्ति देता है कि लिंक को पार्स करने में सक्षम होना चाहिए और यह स्ट्रिंग नाम लाने के बजाय पूरे उदाहरण और नाम पर फ़िल्टर लाता है।

public static Expression<Func<T, bool>> BuildStringMatch<T>(string searchFor) where T : IHasName 
    { 
     return b => 
       string.IsNullOrEmpty(searchFor) || 
       (b.Name != null && 
       (b.Name.Trim().ToLower().StartsWith(searchFor.Trim().ToLower()) || 
       b.Name.Contains(" " + searchFor))); 
    } 

आप इस तरह है कि विधि का उपयोग कर सकते हैं:

IQueryable<Blah> blahs = query.Where(BuildStringMatch<Blah>(searchText)); 

अपने सभी वर्गों है कि आप पर फ़िल्टर जैसे कुछ इंटरफ़ेस को लागू करना चाहते हैं मानता है कि:

public interface IHasName 
    { 
     string Name { get; } 
    } 

आप तो विभिन्न गुणों पर फ़िल्टर करना चाहते हैं, मुझे नहीं लगता कि ऐसा कुछ है जो आप इस तरह के सरल कोड के साथ कर सकते हैं। मेरा मानना ​​है कि आपको खुद को प्रतिबिंब (या प्रतिबिंब का उपयोग करने वाली लाइब्रेरी की सहायता से अभिव्यक्ति) बनाने की आवश्यकता होगी - यह अभी भी संभव है लेकिन बहुत मुश्किल है।

संपादित करें: ऐसा लगता है कि आप की तरह गतिशील व्यवहार की जरूरत है, तो मैं this question करने के लिए dtb के जवाब से कुछ तर्क उधार और इस के साथ आया था:

 IQueryable<Blah> blahs2 = query.Where(BuildStringMatch<Blah>(b => b.Name, searchText)); 
:

public static Expression<Func<T, bool>> BuildStringMatch<T>(Expression<Func<T, string>> property, string searchFor) 
{ 
    var searchForExpression = Expression.Constant(searchFor, typeof(string)); 
    return 
     Expression.Lambda<Func<T, bool>>(
      Expression.OrElse(
       Expression.Call(typeof(string), "IsNullOrEmpty", null, searchForExpression), 
       Expression.AndAlso(
        Expression.NotEqual(property.Body, Expression.Constant(null, typeof(string))), 
        Expression.OrElse(
         Expression.Call(Expression.Call(Expression.Call(property.Body, "Trim", null), "ToLower", null), "StartsWith", null, 
          Expression.Call(Expression.Call(searchForExpression, "Trim", null), "ToLower", null)), 
         Expression.Call(property.Body, "Contains", null, Expression.Call(typeof(string), "Concat", null, Expression.Constant(" "), searchForExpression)) 
        ) 
       ) 
      ), 
      property.Parameters 
     ); 
} 

आप इसे पसंद का प्रयोग करेंगे

यह लंबा और वर्बोज़ है लेकिन आप देख सकते हैं कि यह सीधे सी # कोड में लिखी गई मूल विधि से कैसे समान है। नोट: मैंने इस कोड का परीक्षण नहीं किया है, इसलिए कुछ छोटी समस्याएं हो सकती हैं - लेकिन यह सामान्य विचार है।

+0

उत्तर देने के लिए धन्यवाद, मैंने इस तरह कुछ विचार किया था, लेकिन लचीलापन की कमी बंद है (सब कुछ नहीं मिलान करना चाहते हैं "नाम" कहा जाता है)। अधिक जटिल तरीके से कहां से शुरू करना है इस पर कोई संकेत? – Dan

+0

मैंने कुछ नमूना कोड शामिल करने के लिए अपना जवाब संपादित कर लिया है। यह निश्चित रूप से अधिक जटिल दिखता है लेकिन अगर आपको केवल इसे लिखना है ..... शायद यह इतना बुरा नहीं होगा। –

+0

धन्यवाद, यह वास्तव में सहायक है (शर्म की बात है कि मैं केवल एक बार ऊपर उठ सकता हूं)। हालांकि, मुझे हमारे कोडबेस में कुछ अन्य कोड मिला है जो कि LINQKit नामक लाइब्रेरी का उपयोग करता है जो कुछ ही (मुझे लगता है) एक तटस्थ तरीके से करने के लिए करता है। मैं पूर्ण विवरण के साथ एक नया जवाब जोड़ूंगा। – Dan

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