2009-06-05 10 views
14

मैंने एक फ़िल्टर करने योग्य बाइंडिंगलिस्ट from this source बनाया। यह बहुत अच्छा काम करता है:कैसे एक सिस्टम बनाने के लिए। Linq.Expressions.Expression पसंद के लिए?

list.Filter("Customer == 'Name'"); 

यह क्या करना चाहिए। आंतरिक एक पार्सर की तरह काम करते हैं, जो == या != अभिव्यक्ति को System.Linq.Expressions.Expression में परिवर्तित करता है। इस मामले में, ==System.Linq.Expressions.Expression.Equal बन जाता है।

दुर्भाग्य से System.Linq.Expressions.Expression में एक समान ऑपरेटर नहीं है और मुझे नहीं पता कि इसे कैसे हल किया जाए।

प्रारंभिक कोड इस तरह दिखता है:

private static System.Linq.Expressions.Expression<Func<String, String, bool>> 
    Like_Lambda = (item, search) => item.ToLower().Contains(search.ToLower()); 

private static Func<String, String, bool> Like = Like_Lambda.Compile(); 

उदहारण के लिए:

private static Dictionary<String, Func<Expression, Expression, Expression>> 
    binaryOpFactory = new Dictionary<String, Func<Expression, Expression, Expression>>(); 

static Init() { 
    binaryOpFactory.Add("==", Expression.Equal); 
    binaryOpFactory.Add(">", Expression.GreaterThan); 
    binaryOpFactory.Add("<", Expression.LessThan); 
    binaryOpFactory.Add(">=", Expression.GreaterThanOrEqual); 
    binaryOpFactory.Add("<=", Expression.LessThanOrEqual); 
    binaryOpFactory.Add("!=", Expression.NotEqual); 
    binaryOpFactory.Add("&&", Expression.And); 
    binaryOpFactory.Add("||", Expression.Or); 
} 

तो मैं एक अभिव्यक्ति है कि मैं क्या चाहते हो जाएगा बनाया

Console.WriteLine(like("McDonalds", "donAld")); // true 
Console.WriteLine(like("McDonalds", "King")); // false 

लेकिन binaryOpFactory इस की आवश्यकता है:

Func<Expression, Expression, Expression> 

पूर्वनिर्धारित भाव ठीक होने लगते हैं:

System.Linq.Expressions.Expression.Or; 

किसी को मुझे बताओ कि मेरी अभिव्यक्ति कन्वर्ट करने के लिए कर सकते हैं?

+0

और कैसे अपने तरह काम करता है? मैं आपको अभिव्यक्ति बनाने में मदद कर सकता हूं, लेकिन मुझे यह समझने की ज़रूरत है कि आप इसे पहले कैसे काम करना चाहते हैं ... regex? शामिल? आदि? –

+0

इससे कोई फर्क नहीं पड़ता। अंतिम कार्यान्वयन संभवतः regexp के साथ होगा। असल में मेरे पास एक Func <स्ट्रिंग, स्ट्रिंग, बूल> है कि मैं 2 स्ट्रिंग्स पास करता हूं और वापसी के रूप में सत्य या गलत हो जाता हूं। मेरी समस्या यह है कि मैं System.Linq.Expressions.Expression नेमस्पेस में ऑब्जेक्ट्स के कार्यान्वयन को समझ नहीं पा रहा हूं, जो फंक <अभिव्यक्ति, अभिव्यक्ति, अभिव्यक्ति> (बाइनरीऑप फैक्ट्री के जेनेरिक प्रकार के तर्कों को देखें) जैसा कि मैं नहीं बना सकता मेरी अपनी तुलना –

+0

पुन: टिप्पणी करें: अभिव्यक्ति API को समझना कुछ कर सकता है ... मैं अपने ब्लॉग पर कुछ मूलभूत बातें शामिल करने का प्रयास करता हूं; जॉन की पुस्तक (गहराई में सी #) भी एक उच्च स्तरीय अवलोकन प्रदान करता है। –

उत्तर

15

कुछ की तरह:

static IEnumerable<T> WhereLike<T>(
     this IEnumerable<T> data, 
     string propertyOrFieldName, 
     string value) 
{ 
    var param = Expression.Parameter(typeof(T), "x"); 
    var body = Expression.Call(
     typeof(Program).GetMethod("Like", 
      BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public), 
      Expression.PropertyOrField(param, propertyOrFieldName), 
      Expression.Constant(value, typeof(string))); 
    var lambda = Expression.Lambda<Func<T, bool>>(body, param); 
    return data.Where(lambda.Compile()); 
} 
static bool Like(string a, string b) { 
    return a.Contains(b); // just for illustration 
} 

एक Func<Expression,Expression,Expression> के संदर्भ में:

static Expression Like(Expression lhs, Expression rhs) 
{ 
    return Expression.Call(
     typeof(Program).GetMethod("Like", 
      BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public) 
      ,lhs,rhs); 
} 
+0

अच्छा थाक्स दिखता है, लेकिन मुझे sth की आवश्यकता है। जो एक Func <अभिव्यक्ति, अभिव्यक्ति, अभिव्यक्ति> देता है लेकिन सवाल यह है कि, इस संदर्भ में अभिव्यक्ति 1, अभिव्यक्ति 2 और अभिव्यक्ति 3 क्या है? एक उदाहरण कैसे अभिव्यक्ति। आंतरिक काम आंतरिक रूप से अच्छा होना चाहिए। –

+0

मुझे स्वीकार करना होगा, मैं कोड के पीछे पूरे जादू को समझ नहीं पा रहा हूं, लेकिन कोड का दूसरा भाग एक आकर्षण की तरह काम करता है। –

+0

@Marc क्या यह LINQ से इकाइयों के साथ काम करता है? –

3

मैं IEnumerable और IQueryable के लिए 2 विस्तार तरीकों WhereFilter() बनाया। इस तरह आप इस फ़िल्टर का भी उदाहरण के साथ उपयोग कर सकते हैं इकाई फ्रेमवर्क और सर्वर पर प्रदर्शन फ़िल्टरिंग है।

मैं के आधार पर एक फिल्टर का उपयोग किया * (नहीं?) तो मैं अंतर्निहित Linq तरीकों StartsWith(), EndsWith() और Contains() इस्तेमाल कर सकते हैं। समर्थित प्रारूप: ए *, * एक, * A *, ए * बी

उपयोग:

var filtered = list.WhereFilter(i => i.Name, "a*", "First Name"); 

यहाँ वर्ग की मूल बातें:

/// <summary> 
/// Extension Methods for Filtering on IQueryable and IEnumerable 
/// </summary> 
internal static class WhereFilterExtensions 
{ 
    /// <summary> 
    /// Filters a sequence of values based on a filter with asterix characters: A*, *A, *A*, A*B 
    /// </summary> 
    /// <param name="source"></param> 
    /// <param name="selector">Field to use for filtering. (E.g: item => item.Name)</param> 
    /// <param name="filter">Filter: A*, *A, *A*, A*B</param> 
    /// <param name="fieldName">Optional description of filter field used in error messages</param> 
    /// <returns>Filtered source</returns> 
    public static IEnumerable<T> WhereFilter<T>(this IEnumerable<T> source, Func<T, string> selector, string filter, string fieldName) 
    { 

     if (filter == null) 
      return source; 

     if (selector == null) 
      return source; 

     int astrixCount = filter.Count(c => c.Equals('*')); 
     if (astrixCount > 2) 
      throw new ApplicationException(string.Format("Invalid filter used{0}. '*' can maximum occur 2 times.", fieldName == null ? "" : " for '" + fieldName + "'")); 

     if (filter.Contains("?")) 
      throw new ApplicationException(string.Format("Invalid filter used{0}. '?' is not supported, only '*' is supported.", fieldName == null ? "" : " for '" + fieldName + "'")); 


     // *XX* 
     if (astrixCount == 2 && filter.Length > 2 && filter.StartsWith("*") && filter.EndsWith("*")) 
     { 
      filter = filter.Replace("*", ""); 
      return source.Where(item => selector.Invoke(item).Contains(filter)); 
     } 

     // *XX 
     if (astrixCount == 1 && filter.Length > 1 && filter.StartsWith("*")) 
     { 
      filter = filter.Replace("*", ""); 
      return source.Where(item => selector.Invoke(item).EndsWith(filter)); 
     } 

     // XX* 
     if (astrixCount == 1 && filter.Length > 1 && filter.EndsWith("*")) 
     { 
      filter = filter.Replace("*", ""); 
      return source.Where(item => selector.Invoke(item).StartsWith(filter)); 
     } 

     // X*X 
     if (astrixCount == 1 && filter.Length > 2 && !filter.StartsWith("*") && !filter.EndsWith("*")) 
     { 
      string startsWith = filter.Substring(0, filter.IndexOf('*')); 
      string endsWith = filter.Substring(filter.IndexOf('*') + 1); 

      return source.Where(item => selector.Invoke(item).StartsWith(startsWith) && selector.Invoke(item).EndsWith(endsWith)); 
     } 

     // XX 
     if (astrixCount == 0 && filter.Length > 0) 
     { 
      return source.Where(item => selector.Invoke(item).Equals(filter)); 
     } 

     // * 
     if (astrixCount == 1 && filter.Length == 1) 
      return source; 

     // Invalid Filter 
     if (astrixCount > 0)    
      throw new ApplicationException(string.Format("Invalid filter used{0}.", fieldName == null ? "" : " for '" + fieldName + "'")); 

     // Empty string: all results 
     return source; 


    } 

    /// <summary> 
    /// Filters a sequence of values based on a filter with asterix characters: A*, *A, *A*, A*B 
    /// </summary> 
    /// <param name="source"></param> 
    /// <param name="selector">Field to use for filtering. (E.g: item => item.Name)  </param> 
    /// <param name="filter">Filter: A*, *A, *A*, A*B</param> 
    /// <param name="fieldName">Optional description of filter field used in error messages</param> 
    /// <returns>Filtered source</returns> 
    public static IQueryable<T> WhereFilter<T>(this IQueryable<T> source, Expression<Func<T, string>> selector, string filter, string fieldName) 
    { 

     if (filter == null) 
      return source; 

     if (selector == null) 
      return source; 

     int astrixCount = filter.Count(c => c.Equals('*')); 
     if (astrixCount > 2) 
      throw new ApplicationException(string.Format("Invalid filter used{0}. '*' can maximum occur 2 times.", fieldName==null?"":" for '" + fieldName + "'")); 

     if (filter.Contains("?"))    
      throw new ApplicationException(string.Format("Invalid filter used{0}. '?' is not supported, only '*' is supported.", fieldName == null ? "" : " for '" + fieldName + "'")); 

     // *XX* 
     if (astrixCount == 2 && filter.Length > 2 && filter.StartsWith("*") &&   filter.EndsWith("*")) 
     { 
      filter = filter.Replace("*", ""); 
      return source.Where(
       Expression.Lambda<Func<T, bool>>(
        Expression.Call(selector.Body, "Contains", null, Expression.Constant(filter)), 
        selector.Parameters[0] 
       ) 
      ); 
     } 

     // *XX 
     if (astrixCount == 1 && filter.Length > 1 && filter.StartsWith("*")) 
     { 
      filter = filter.Replace("*", ""); 
      return source.Where(
       Expression.Lambda<Func<T, bool>>(
        Expression.Call(selector.Body, "EndsWith", null, Expression.Constant(filter)), 
        selector.Parameters[0] 
       ) 
      ); 
     } 

     // XX* 
     if (astrixCount == 1 && filter.Length > 1 && filter.EndsWith("*")) 
     { 
      filter = filter.Replace("*", ""); 
      return source.Where(
       Expression.Lambda<Func<T, bool>>(
        Expression.Call(selector.Body, "StartsWith", null,   Expression.Constant(filter)), 
        selector.Parameters[0] 
       ) 
      ); 
     } 

     // X*X 
     if (astrixCount == 1 && filter.Length > 2 && !filter.StartsWith("*") && !filter.EndsWith("*")) 
     { 
      string startsWith = filter.Substring(0, filter.IndexOf('*')); 
      string endsWith = filter.Substring(filter.IndexOf('*') + 1); 

      return source.Where(
       Expression.Lambda<Func<T, bool>>(
        Expression.Call(selector.Body, "StartsWith", null,   Expression.Constant(startsWith)), 
        selector.Parameters[0] 
       ) 
      ).Where(
       Expression.Lambda<Func<T, bool>>(
        Expression.Call(selector.Body, "EndsWith", null,   Expression.Constant(endsWith)), 
        selector.Parameters[0] 
       ) 
      ); 
     } 

     // XX 
     if (astrixCount == 0 && filter.Length > 0) 
     { 
      return source.Where(
       Expression.Lambda<Func<T, bool>>(
        Expression.Equal(selector.Body, Expression.Constant(filter)), 
        selector.Parameters[0] 
       ) 
      ); 
     } 

     // * 
     if (astrixCount == 1 && filter.Length == 1) 
      return source; 

     // Invalid Filter 
     if (astrixCount > 0) 
      throw new ApplicationException(string.Format("Invalid filter used{0}.", fieldName == null ? "" : " for '" + fieldName + "'")); 

     // Empty string: all results 
     return source; 

    } 
} 
+0

द्वारा 'उदा। इकाई फ्रेमवर्क और सर्वर पर प्रदर्शित फ़िल्टरिंग है 'क्या आप डेटाबेस में मतलब है - यानी: एसक्यूएल में अनुवादित? मैं इंप्रेशन के तहत था 'चयनकर्ता। इन्वोक' ने एसएफ को एसक्यूएल से अनुवाद करने से रोका? – JoeBrockhaus

+0

हां, अभिव्यक्ति ट्री को 'LINQ प्रदाता' द्वारा परिवर्तित किया जाता है। एनीटी फ्रेमवर्क का उपयोग करते समय, LINQ से Entities प्रदाता इसे SQL स्ट्रिंग में परिवर्तित कर देगा जो डेटाबेस पर निष्पादित किया जाएगा। नतीजा कोई ऐरे नहीं होगा लेकिन एक आईनेमेरेबल होगा जो डेटा रीडर के माध्यम से चलता है। –

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