/// <summary>
        /// Applies filtering to the IQueryable provided.
        /// </summary>
        /// <param name="query">The IQueryable that is to be filtered.</param>
        /// <param name="filteringForm">The filtering form.</param>
        /// <returns>A filtered IQueryable.</returns>
        public virtual IQueryable <TEntity> ApplyWhere(IQueryable <TEntity> query, IFilterForm filteringForm)
        {
            var term  = filteringForm.GetTerm();
            var words = GetWords(term);

            if (_getApplyWhere != null && term != null)
            {
                var applyWhere = _getApplyWhere(term);
                query = applyWhere(query);
            }

            // create predicate1 (word expressions)
            Expression <Func <TEntity, bool> > predicate1 = null;

            if (_getFreeWordPredicate != null)
            {
                foreach (var word in words)
                {
                    predicate1 = AddExpression(predicate1, _getFreeWordPredicate(word), WordCombiner == WordSearchCombiner.And ? ExpressionType.AndAlso : ExpressionType.OrElse);
                }
            }

            // create predicate2 (sentence expression)
            Expression <Func <TEntity, bool> > predicate2 = null;

            if (_getFreeSentencePredicate != null && term != null)
            {
                predicate2 = _getFreeSentencePredicate(term);
            }

            // create predicate1 OR predicate2 as currentBody
            Expression <Func <TEntity, bool> > textPredicate = null;

            if (predicate1 != null && predicate2 != null)
            {
                textPredicate = predicate1.Or(predicate2);
            }
            else if (predicate1 != null)
            {
                textPredicate = predicate1;
            }
            else if (predicate2 != null)
            {
                textPredicate = predicate2;
            }

            var localizations = GetLocalizationDictionary();
            Expression <Func <TEntity, bool> > keywordPredicate = null;

            for (int i = 0; i < words.Length; i++)
            {
                for (int j = i; j < words.Length; j++)
                {
                    // from i to j
                    var word = string.Join(" ", words, i, j - i + 1);

                    Expression <Func <TEntity, bool> > predicate;
                    if (_predefinedPredicates.TryGetValue(word, out predicate))
                    {
                        keywordPredicate = AddExpression(keywordPredicate, predicate, ExpressionType.AndAlso);
                    }

                    Expression <Func <TEntity, bool> > localizedPredicate;
                    if (localizations.TryGetValue(word, out localizedPredicate))
                    {
                        keywordPredicate = AddExpression(keywordPredicate, localizedPredicate, ExpressionType.AndAlso);
                    }
                }
            }

            // create textPredicate OR keywordPredicate as currentBody
            Expression <Func <TEntity, bool> > currentBody = null;

            if (textPredicate != null && keywordPredicate != null)
            {
                currentBody = textPredicate.Or(keywordPredicate);
            }
            else if (textPredicate != null)
            {
                currentBody = textPredicate;
            }
            else if (keywordPredicate != null)
            {
                currentBody = keywordPredicate;
            }

            Expression <Func <TEntity, bool> > parameterPredicate = null;
            var parameters = filteringForm.GetAdditionalFilters();

            if (parameters != null)
            {
                foreach (var propertyComparison in parameters)
                {
                    var comparisonType        = propertyComparison.GetComparisonType();
                    var memberAccess          = propertyComparison.GetMemberAccess(_parameter);
                    var memberAccessor        = memberAccess.MemberAccessor;
                    var propertyType          = memberAccess.MemberType;
                    var unwrappedPropertyType = propertyType.GetTypeInfo().IsGenericType&& propertyType.GetGenericTypeDefinition() == typeof(Nullable <>)
                  ? propertyType.GetTypeInfo().GetGenericArguments()[0]
                  : propertyType;
                    var propertyValue = propertyComparison.GetValue();


                    object convertedPropertyValue;
                    if (propertyValue is string && (memberAccessor.Type == typeof(Guid) || memberAccessor.Type == typeof(Guid? )))
                    {
                        convertedPropertyValue = Guid.Parse((string)propertyValue);
                    }
                    else
                    {
                        convertedPropertyValue = Convert.ChangeType(propertyValue, memberAccessor.Type);
                    }
                    var parameterizedPropertyValue = ExpressionHelper.WrappedConstant(memberAccessor.Type, convertedPropertyValue);

                    Expression left = null;
                    switch (comparisonType)
                    {
                    case ComparisonType.Equal:
                        left = Expression.Equal(memberAccessor, parameterizedPropertyValue);
                        break;

                    case ComparisonType.GreaterThan:
                        left = Expression.GreaterThan(memberAccessor, parameterizedPropertyValue);
                        break;

                    case ComparisonType.GreaterThanOrEqual:
                        left = Expression.GreaterThanOrEqual(memberAccessor, parameterizedPropertyValue);
                        break;

                    case ComparisonType.LessThan:
                        left = Expression.LessThan(memberAccessor, parameterizedPropertyValue);
                        break;

                    case ComparisonType.LessThanOrEqual:
                        left = Expression.LessThanOrEqual(memberAccessor, parameterizedPropertyValue);
                        break;

                    case ComparisonType.StartsWith:
                        left = Expression.Call(memberAccessor, StartsWith, parameterizedPropertyValue);
                        break;

                    case ComparisonType.Contains:
                        left = Expression.Call(memberAccessor, Contains, parameterizedPropertyValue);
                        break;

                    default:
                        throw new InvalidOperationException($"Invalid comparison type '{comparisonType}'.");
                    }

                    parameterPredicate = AddExpression(
                        parameterPredicate,
                        Expression.Lambda <Func <TEntity, bool> >(left, _parameter),
                        ExpressionType.AndAlso);
                }
            }

            // combine with current body as needed
            if (currentBody != null && parameterPredicate != null)
            {
                currentBody = currentBody.And(parameterPredicate);
            }
            else if (parameterPredicate != null)
            {
                currentBody = parameterPredicate;
            }


            if (currentBody != null)
            {
                return(query.Where(currentBody));
            }
            else
            {
                return(query);
            }
        }