internal virtual Expression <Func <T, bool> > ToWhereClause <T>(ParameterExpression parameterExpression) { // default implementation is to search all fields specified by the // DefaultSearchField option in SearchConfigurationOptions var entityConfig = Options.Entity <T>(); if (entityConfig.DefaultSearchFields == null || !entityConfig.DefaultSearchFields.Any()) { throw new InvalidOperationException($"There are no DefaultSearchFields specified for the enity of type { typeof(T).FullName }"); } Expression expr = null; foreach (var searchField in entityConfig.DefaultSearchFields) { Expression currentExpression; var oldParameter = searchField.Parameters.First(); var convertParameter = new ParameterConversionVisitor(parameterExpression, oldParameter); var newBody = convertParameter.Visit(searchField.Body); if (searchField.ReturnType == typeof(string)) { // we'll use the null-coalescing operator so we don't get null refernces var nullCoalesceExpression = Expression.Coalesce(newBody, Expression.Constant(string.Empty)); currentExpression = Expression.Call(nullCoalesceExpression, typeof(string).GetMethod("Contains", new[] { typeof(string) }), Expression.Constant(Text, typeof(string))); } else { throw new InvalidOperationException("Currently only expressions of type string are supported"); } if (expr == null) { expr = currentExpression; } else { // we combine the previous expression with the current in an OR clause expr = Expression.Or(expr, currentExpression); } } return(Expression.Lambda <Func <T, bool> >(expr, parameterExpression)); }
private Expression <Func <T, bool> > GetParameterConstantExpression <T>( ParameterExpression parameterExpression, EntitySearchConfiguration <T> entityConfig) { // we'll need this later in a couple places: var toUpperMethod = typeof(string).GetMethod("ToUpper", Type.EmptyTypes); var searchField = entityConfig.GetTypeMapping(Left.Text); if (searchField == null) { throw new SearchParseException($"Unable to find a field with name { Left.Text }"); } var oldParameter = searchField.Parameters.First(); var convertParameter = new ParameterConversionVisitor(parameterExpression, oldParameter); var leftExpression = convertParameter.Visit(searchField.Body); // the right side is easy since it is a constant, // however we have to try to force it to the correct type var leftType = leftExpression.Type; bool isEnumerable = false; if (leftType.IsGenericType && typeof(IEnumerable <>).IsAssignableFrom(leftType.GetGenericTypeDefinition())) { isEnumerable = true; // if IEnumerable, get the underlying type leftType = leftType.GetGenericArguments().Single(); } Expression rightExpression; if (leftType == typeof(string)) { // special case for strings, we don't have to convert them // but we should uppercase them rightExpression = Expression.Call( Expression.Constant(Right.Text), toUpperMethod); } else { // any other types we have to convert var typeConverter = System.ComponentModel.TypeDescriptor.GetConverter(leftType); var rightValue = typeConverter.ConvertFromString(Right.Text); rightExpression = Expression.Constant(rightValue, leftType); } // now all that's left is the operator, which should be supplied by a base class Expression expr; // special case for IEnumerable types, we need to do an ANY if (isEnumerable) { var anyExpression = typeof(Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static) .Where(x => x.Name == "Any") .Single(mi => mi.GetParameters().Count() == 2) .MakeGenericMethod(leftType); var newParam = Expression.Parameter(leftType); var newLeftExpression = leftType == typeof(string) ? (Expression)Expression.Call(newParam, toUpperMethod) : newParam; var subExpression = CreateOperatorExpression <T>(newLeftExpression, rightExpression, parameterExpression); var func = Expression.Lambda( subExpression, newParam); expr = Expression.Call( anyExpression, leftExpression, func); } else { // don't forget to uppercase string! if (leftType == typeof(string)) { leftExpression = Expression.Call(leftExpression, toUpperMethod); } expr = CreateOperatorExpression <T>(leftExpression, rightExpression, parameterExpression); } return(Expression.Lambda <Func <T, bool> >(expr, parameterExpression)); }