예제 #1
0
        private static IEnumerable <MemberInfo> GetAvailableMembers(Type type, CompatiblityMode mode)
        {
            var flags = BindingFlags.Public | BindingFlags.Instance;

            if (mode == CompatiblityMode.Strict)
            {
                return(type
                       .GetProperties(flags)
                       .Where(m => m.CanWrite && m.MemberType == MemberTypes.Property));
            }
            else
            {
                return(type
                       .GetFields(flags).Cast <MemberInfo>()
                       .Concat(type.GetProperties(flags)).ToArray()
                       .Where(m => m.MemberType == MemberTypes.Property || m.MemberType == MemberTypes.Field));
            }
        }
        /// <summary>
        /// Filters entries by all their string fields
        /// </summary>
        /// <param name="searchQuery">The search query by which the entries should be filtered</param>
        /// <returns>A filtered collection of entries</returns>
        public static IQueryable <T> Search <T>(this IQueryable <T> data, string searchQuery, CompatiblityMode mode = CompatiblityMode.Strict)
        {
            // Simply return the entire collection, if no search query is specified
            if (string.IsNullOrWhiteSpace(searchQuery))
            {
                return(data);
            }

            var matches = data;

            // Split the search query and construct predicates for each
            foreach (var part in searchQuery.ToUpperInvariant().Split())
            {
                if (!string.IsNullOrWhiteSpace(part))
                {
                    matches = matches.Where(SearchHelper.ConstructSearchPredicate <T>(part.Trim(), mode));
                }
            }

            return(matches);
        }
예제 #3
0
        /// <summary>
        /// Constructs an expression that is used to filter entries and execute a search for the specified query
        /// on all string type fields of an entity
        /// </summary>
        /// <param name="query">The query to be searched for in all string fields</param>
        /// <returns>An expression that can be used in queries to the DB context</returns>
        ///
        /// Example:
        /// For an entity with string properties `Name` and `Address`, the resulting expression
        /// is something like this:
        ///
        /// `x => x.Name.ToLower().Contains(query) || x.Address.ToLower().Contains(query)`
        ///
        internal static Expression <Func <T, bool> > ConstructSearchPredicate <T>(string query, CompatiblityMode mode)
        {
            // Create constant with query
            var constant = Expression.Constant(query);

            // Get all object properties
            var type = typeof(T);

            // Input parameter (e.g. "c => ")
            var parameter = Expression.Parameter(type, "c");

            // Get appropriate members to test
            var members = GetAvailableMembers(type, mode);

            // Construct expression
            Expression finalExpression = null;

            foreach (var m in members)
            {
                // Get type of p
                var underlyingType = m.GetUnderlyingType();

                // In Strict mode, only string properties are taken into account, otherwise all props and fields are (except collections)
                if ((mode == CompatiblityMode.Strict && underlyingType == typeof(string)) || (mode == CompatiblityMode.All && !underlyingType.IsCollection()))
                {
                    // Express a member (e.g. "c.<member>" )
                    Expression memberExpression;
                    if (m.MemberType == MemberTypes.Field)
                    {
                        memberExpression = Expression.Field(parameter, m.Name);
                    }
                    else
                    {
                        memberExpression = Expression.Property(parameter, m.Name);
                    }

                    // Get query expression (e.g. "c.<member>.ToString().ToUpperInvariant().Contains(<constant>)")
                    var partialExpression = GetQueryExpression(memberExpression, constant, underlyingType, mode);

                    // Handle case when no OR operation can be constructed (it's the first condition)
                    if (finalExpression == null)
                    {
                        finalExpression = partialExpression;
                    }
                    else
                    {
                        finalExpression = Expression.OrElse(finalExpression, partialExpression);
                    }
                }
            }


            // Return the constructed expression
            return(Expression.Lambda <Func <T, bool> >(finalExpression, parameter));
        }
예제 #4
0
        /// <summary>
        /// Constructs an expression to query the property for the specified search term
        /// </summary>
        /// <param name="propertyExpression">The expression of the property being tested</param>
        /// <param name="queryConstant">The expression representing the search term</param>
        /// <param name="propertyType">The type of the property to be tested</param>
        /// The resulting expression will test the property for inclusion of the query
        /// (e.g. "c.<property>.ToString().ToLowerInvariant().Contains(<queryConstant>)")
        private static Expression GetQueryExpression(Expression propertyExpression, Expression queryConstant, Type propertyType, CompatiblityMode mode)
        {
            // Check that property value is not null (or default) (e.g. "c.<property> != null")
            Expression nullCheckExpression = null;

            // Value types can safely be operated on, since they have non-null default values
            if (!propertyType.IsValueType)
            {
                nullCheckExpression = Expression.NotEqual(propertyExpression, Expression.Constant(null, propertyType));
            }

            var transformedProperty = propertyExpression;

            // In strict mode, ToString and ToUpperInvariant cannot be used (provider might not know how to translate them)
            if (mode == CompatiblityMode.Strict)
            {
                // Run uppercase method on property (e.g. "c.<property>.ToUpper()")
                transformedProperty = Expression.Call(transformedProperty, UpperMethod);
            }
            else
            {
                // Find the ToString method that should be executed for the specific type
                var toStringMethod = propertyType.GetMethod("ToString", new Type[0]);

                // Run ToString method on property (e.g. "c.<property>.ToString()")
                transformedProperty = Expression.Call(propertyExpression, toStringMethod);

                // Run uppercase method on property (e.g. "c.<property>.ToString().ToUpperInvariant()")
                transformedProperty = Expression.Call(transformedProperty, UpperInvariantMethod);
            }

            // Run contains on property with provided query (e.g. "c.<property>.ToString().ToUpperInvariant().Contains(<query>)")
            transformedProperty = Expression.Call(transformedProperty, ContainsMethod, queryConstant);

            if (nullCheckExpression == null)
            {
                return(transformedProperty);
            }
            else
            {
                return(Expression.AndAlso(nullCheckExpression, transformedProperty));
            }
        }