2011-04-14 13 views
12

में ऑपरेटर की तरह मैं LINQ से ऑब्जेक्ट्स में LIKE ऑपरेटर का अनुकरण करने की कोशिश कर रहा हूं। यहाँ मेरी कोड:LINQ से ऑब्जेक्ट्स

List<string> list = new List<string>(); 
list.Add("line one"); 
list.Add("line two"); 
list.Add("line three"); 
list.Add("line four"); 
list.Add("line five"); 
list.Add("line six"); 
list.Add("line seven"); 
list.Add("line eight"); 
list.Add("line nine"); 
list.Add("line ten"); 

string pattern = "%ine%e"; 

var res = from i in list 
      where System.Data.Linq.SqlClient.SqlMethods.Like(i, pattern) 
       select i; 

यह मुझे क्योंकि System.Data.Linq.SqlClient.SqlMethods.Like का केवल SQL में अनुवाद के लिए है परिणाम नहीं मिला।

क्या एसक्यूएल LIKE ऑपरेटर के समान कुछ भी LINQ से ऑब्जेक्ट्स वर्ल्ड में मौजूद है?

उत्तर

18

मैं, एक है कि आसानी से मौजूद है पता नहीं है, लेकिन अगर आप नियमित अभिव्यक्ति से परिचित हैं आप लिख सकते हैं अपनी खुद की:

using System; 
using System.Text.RegularExpressions; 

public static class MyExtensions 
{ 
    public static bool Like(this string s, string pattern, RegexOptions options = RegexOptions.IgnoreCase) 
    { 
     return Regex.IsMatch(s, pattern, options); 
    } 
} 

और फिर अपने कोड में:

string pattern = ".*ine.*e"; 
var res = from i in list 
    where i.Like(pattern) 
    select i; 
+0

वाह! दूसरों की तुलना में इसका सबसे बढ़िया जवाब! आपका बहुत बहुत धन्यवाद –

6

यह स्निपेट एसक्यूएल के व्यवहार और वाक्यविन्यास की नकल करेगा।

public static bool IsSqlLikeMatch(string input, string pattern) 
{ 
    /* Turn "off" all regular expression related syntax in 
    * the pattern string. */ 
    pattern = Regex.Escape(pattern); 

    /* Replace the SQL LIKE wildcard metacharacters with the 
    * equivalent regular expression metacharacters. */ 
    pattern = pattern.Replace("%", ".*?").Replace("_", "."); 

    /* The previous call to Regex.Escape actually turned off 
    * too many metacharacters, i.e. those which are recognized by 
    * both the regular expression engine and the SQL LIKE 
    * statement ([...] and [^...]). Those metacharacters have 
    * to be manually unescaped here. */ 
    pattern = pattern.Replace(@"\[", "[").Replace(@"\]", "]").Replace(@"\^", "^"); 

    return Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase); 
} 

एक साथ मारपीट की विस्तार विधि है कि IEnumerable<T>.Where विधि की तरह काम करेगा:: आप एक Linq बयान भीतर उपयोग के लिए यह लपेट कर सकते हैं अपने खुद की एक लैम्ब्डा या विस्तार विधि में

public static IEnumerable<T> Like<T>(this IEnumerable<T> source, Func<T, string> selector, string pattern) 
{ 
    return source.Where(t => IsSqlLikeMatch(selector(t), pattern)); 
} 

कौन सा में होगा बारी तुम इतनी तरह अपने बयान फ़ॉर्मेट करने के लिए अनुमति देते हैं:

string pattern = "%ine%e"; 
var res = list.Like(s => s, pattern); 

संपादित एक इंप्रोव एड कार्यान्वयन, किसी को भी ठोकर मारना चाहिए और इस कोड का उपयोग करना चाहते हैं। यह प्रत्येक आइटम के बजाय एक बार regex को परिवर्तित और संकलित करता है और ऊपर से regex करने के लिए रूपांतरण कुछ बग है।

public static class LikeExtension 
{ 
    public static IEnumerable<T> Like<T>(this IEnumerable<T> source, Func<T, string> selector, string pattern) 
    { 
     var regex = new Regex(ConvertLikeToRegex(pattern), RegexOptions.IgnoreCase); 
     return source.Where(t => IsRegexMatch(selector(t), regex)); 
    } 

    static bool IsRegexMatch(string input, Regex regex) 
    { 
     if (input == null) 
      return false; 

     return regex.IsMatch(input); 
    } 

    static string ConvertLikeToRegex(string pattern) 
    { 
     StringBuilder builder = new StringBuilder(); 
     // Turn "off" all regular expression related syntax in the pattern string 
     // and add regex begining of and end of line tokens so '%abc' and 'abc%' work as expected 
     builder.Append("^").Append(Regex.Escape(pattern)).Append("$"); 

     /* Replace the SQL LIKE wildcard metacharacters with the 
     * equivalent regular expression metacharacters. */ 
     builder.Replace("%", ".*").Replace("_", "."); 

     /* The previous call to Regex.Escape actually turned off 
     * too many metacharacters, i.e. those which are recognized by 
     * both the regular expression engine and the SQL LIKE 
     * statement ([...] and [^...]). Those metacharacters have 
     * to be manually unescaped here. */ 
     builder.Replace(@"\[", "[").Replace(@"\]", "]").Replace(@"\^", "^"); 

     // put SQL LIKE wildcard literals back 
     builder.Replace("[.*]", "[%]").Replace("[.]", "[_]"); 

     return builder.ToString(); 
    } 
} 
+0

यह सबसे अच्छा जवाब है, धन्यवाद! –

+1

मुझे वास्तव में ध्यान रखना चाहिए कि मैं व्यक्तिगत रूप से IsSqlLikeMatch के कार्यान्वयन के लिए क्रेडिट नहीं ले सकता। साल पहले interwebs पर पाया। मुझे सबसे अच्छा एट्रिब्यूशन मिल सकता है: http://bytes.com/topic/c-sharp/answers/253519-using-regex-create-sqls-like-like- समारोह मुझे लगता है कि मूल – dkackman

5

आप पैटर्न के लिए regex का उपयोग करें, और तब पुनरावृति और मैच खोजने के लिए विस्तार विधि Where उपयोग करना होगा।

तो अपने कोड इस तरह खत्म करना चाहिए:

string pattern = @".*ine.*e$"; 

var res = list.Where(e => Regex.IsMatch(e, pattern)); 

आप Regex से अपरिचित हैं, इस पर लिखा है: (। *)

पहले 0 या अधिक वर्ण ऑफ़लाइन द्वारा पीछा किया (ऑफ़लाइन) तो 0 या अधिक वर्ण (। *) तो और (ई)

var query = from c in ctx.Customers 

      where c.City.StartsWith("Lo") 

      select c; 

will generate this SQL statement: 
SELECT CustomerID, CompanyName, ... 
FROM dbo.Customers 
WHERE City LIKE [Lo%] 

:, और स्ट्रिंग ($)

1

1. String.StartsWith या String.Endswith

का उपयोग करते हुए निम्न क्वेरी लेखन के अंत होना चाहिए जो वही है जो हम चाहते थे। स्ट्रिंग के साथ चला जाता है। EndsWith।

लेकिन, हम ग्राहक को "L_n%" जैसे शहर के नाम से पूछना चाहते हैं? (कुछ चरित्रों की तुलना में 'एन' और शेष नाम की तुलना में पूंजी 'एल' से शुरू होता है)। क्वेरी का उपयोग

var query = from c in ctx.Customers 

      where c.City.StartsWith("L") && c.City.Contains("n") 

      select c; 

generates the statement: 
SELECT CustomerID, CompanyName, ... 
FROM dbo.Customers 
WHERE City LIKE [L%] 
AND  City LIKE [%n%] 

जो बिल्कुल वही नहीं है जो हम चाहते थे, और थोड़ा और जटिल भी।

2. SqlMethods.Like विधि

System.Data.Linq.SqlClient नेम स्पेस में खुदाई का उपयोग करना, मैं एक छोटे से सहायक वर्ग SqlMethods कहा जाता है, जो इस प्रकार की स्थितियों में बहुत उपयोगी हो सकता है पाया। SqlMethods एक पद्धति की तरह कहा जाता है, कि SQL क्वेरी के लिए एक Linq में इस्तेमाल किया जा सकता है:

var query = from c in ctx.Customers 

      where SqlMethods.Like(c.City, "L_n%") 

      select c; 

इस विधि (इस उदाहरण में ग्राहक के शहर) की जाँच करने के स्ट्रिंग अभिव्यक्ति हो जाता है और पैटर्न जिसके खिलाफ परीक्षण करने के लिए प्रदान की जाती है उसी तरह आप SQL में एक LIKE क्लॉज लिखेंगे।

ऊपर क्वेरी का उपयोग करना आवश्यक SQL विवरण उत्पन्न:

SELECT CustomerID, CompanyName, ... 
FROM dbo.Customers 
WHERE City LIKE [L_n%] 

स्रोत: http://blogs.microsoft.co.il/blogs/bursteg/archive/2007/10/16/linq-to-sql-like-operator.aspx

+1

उत्तर के लिए धन्यवाद ! लेकिन आप किस बारे में हैं? दुर्भाग्य से हम SqlMethod का उपयोग नहीं कर सकते हैं। LINQ में मेरे प्रश्न –

+0

में उल्लिखित वस्तुओं के अनुसार, डुनो क्यों मतदान किया गया यह मेरे लिए काम करता है। – awiebe

+0

@awiebe: क्या आप इसे LINQ से OBjects के साथ काम करने के लिए मजबूर कर रहे हैं? –

0

मुझे नहीं पता कि यह मौजूद है या नहीं, लेकिन यहां मैंने न्यूथ-मॉरिस-प्रैट एल्गोरिदम का उपयोग करके एक विस्तार विधि का कार्यान्वयन किया है।

public static IEnumerable<T> Like<T>(this IEnumerable<T> lista, Func<T, string> type, string pattern) 
      { 

       int[] pf = prefixFunction(pattern); 

       foreach (T e in lista) 
       { 
        if (patternKMP(pattern, type(e), pf)) 
         yield return e; 
       } 

      } 

      private static int[] prefixFunction(string p) 
      { 


       int[] pf = new int[p.Length]; 
       int k = pf[0] = -1; 


       for (int i = 1; i < p.Length; i++) 
       { 
        while (k > -1 && p[k + 1] != p[i]) 
         k = pf[k]; 

        pf[i] = (p[k + 1] == p[i]) ? ++k : k; 
       } 
       return pf; 

      } 

      private static bool patternKMP(string p, string t, int[] pf) 
      { 

       for (int i = 0, k = -1; i < t.Length; i++) 
       { 

        while (k > -1 && p[k + 1] != t[i]) 
         k = pf[k]; 

        if (p[k + 1] == t[i]) 
         k++; 

        if (k == p.Length - 1) 
         return true;  
       } 

       return false; 

      }