/// <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="searchQuery">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 <TObject, bool> > ConstructSearchPredicate <TObject>(string searchQuery, params Expression <Func <TObject, string> >[] members) { // Create constant with query var constant = Expression.Constant(searchQuery); // Input parameter (e.g. "c => ") var parameter = Expression.Parameter(typeof(TObject), "c"); // Construct expression Expression finalExpression = null; foreach (var m in members) { // Visit the provided expression and replace the input parameter with the one defined above ("c") // e.g. (from "x.Something" we get "c.Something") var memberExpression = new ExpressionParameterVisitor(m.Parameters.First(), parameter) .VisitAndConvert(m.Body, nameof(ConstructSearchPredicate)); // Get query expression var partialExpression = GetQueryExpression(memberExpression, constant, memberExpression.Type, CompatiblityMode.Strict); // Handle case when no OR operation can be constructed if (finalExpression == null) { finalExpression = partialExpression; } else { finalExpression = Expression.OrElse(finalExpression, partialExpression); } } // Return the constructed expression return(Expression.Lambda <Func <TObject, bool> >(finalExpression, parameter)); }
/// <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="searchQuery">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)` /// private static Expression <Func <T, bool> > ConstructSearchPredicate <T>(string searchQuery, params Expression <Func <T, string> >[] fields) { // Create constant with query var constant = Expression.Constant(searchQuery); // Input parameter (e.g. "c => ") var parameter = Expression.Parameter(typeof(T), "c"); // Find methods that will be run on each property var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) }); var lowerMethod = typeof(string).GetMethod("ToLowerInvariant", new Type[0]); // Construct expression Expression finalBody = null; foreach (var f in fields) { // Visit the provided expression and replace the input parameter with the one defined above ("c") // e.g. (from "x.Something" we get "c.Something") var propertyExpression = new ExpressionParameterVisitor(f.Parameters.First(), parameter) .VisitAndConvert(f.Body, nameof(ConstructSearchPredicate)); // Run lowercase method on property (e.g. "c.<property>.ToLowerInvariant()") var transformedProperty = Expression.Call(propertyExpression, lowerMethod); // Run contains on property with provided query (e.g. "c.<property>.ToLowerInvariant().Contains(<query>)") transformedProperty = Expression.Call(transformedProperty, containsMethod, constant); // Handle case when no OR operation can be constructed if (finalBody == null) { finalBody = transformedProperty; } else { finalBody = Expression.Or(finalBody, transformedProperty); } } // Return the constructed expression return(Expression.Lambda <Func <T, bool> >(finalBody, parameter)); }