How to create a System.Linq.Expressions.Expression for Like?

后端 未结 2 1816
北海茫月
北海茫月 2020-12-13 05:31

I created a filterable BindingList from this source. It works great:

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

does what it should. The intern

相关标签:
2条回答
  • 2020-12-13 06:04

    I created 2 extension methods WhereFilter() for IEnumerable and IQueryable. At this way you can use this filter also with e.g. Entity Framework and is the filtering performed on the server.

    I used a filter based on * (not ?) so i could use the underlaying Linq methods StartsWith(), EndsWith() and Contains(). Supported formats: A*, *A, *A*, A*B

    Usage:

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

    Here the basics of the class:

    /// <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 讨论(0)
  • 2020-12-13 06:17

    Something like:

    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
    }
    

    In terms of a 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 讨论(0)
提交回复
热议问题