Search code examples
c#asp.net-web-apilambdaexpression-trees

Object reference error in Anonymously Hosted DynamicMethods Assembly


I have the following code where I need to implement fulltext search on a list of objects. I managed to get this working in a console application. The problem is when I start using this in WebAPI application. I get a error message saying Object reference not set to an instance of an object. and the Source is Anonymously Hosted DynamicMethods Assembly. It seems like some properties of the Type T are null and when it is accessed the error pops up. Can anyone tell me whether my understanding is correct or not and if its correct then can you also help me figuring out how to get rid of the null properties?

public static IEnumerable<T> FullTextSearch<T>(this List<T> list, string searchKey)
        {
            ParameterExpression parameter = Expression.Parameter(typeof(T), "c");
            MethodInfo containsMethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
            var publicProperties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
                .Where(p => p.PropertyType == typeof(string));
            Expression orExpressions = null;
            foreach (MethodCallExpression callContainsMethod in 
                               from property in publicProperties
                               let myProperty =Expression.Property(parameter, property.Name)
                               select Expression.Call(myProperty, "Contains", null, Expression.Constant(searchKey)))
            {
                if (orExpressions == null)
                {
                    orExpressions = callContainsMethod;
                }
                else
                {
                    orExpressions = Expression.Or(orExpressions, callContainsMethod);
                }
            }

            IQueryable<T> queryable = list.AsQueryable<T>();
            MethodCallExpression whereCallExpression = Expression.Call(
                typeof(Queryable),
                "Where",
                new Type[] { queryable.ElementType },
                queryable.Expression,
                Expression.Lambda<Func<T, bool>>(orExpressions, new ParameterExpression[] { parameter }));
            var results = queryable.Provider.CreateQuery<T>(whereCallExpression).ToList();
            return results;
        }

Solution

  • Ok Guys, I figured it out. Here is the code just in case someone needs it.

     public static List<T> FullTextSearch<T>(this List<T> list, string searchKey)
            {
                ParameterExpression parameter = Expression.Parameter(typeof(T), "c");
                MethodInfo containsMethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
                var publicProperties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
                    .Where(p => p.PropertyType == typeof(string));
                Expression orExpressions = null;
    
                foreach (var callContainsMethod in from property in publicProperties
                                                                    let myProperty = Expression.Property(parameter, property.Name)
                                                                    let myExpression = Expression.Call(myProperty, "Contains", null, Expression.Constant(searchKey))
                                                                    let myNullExp = Expression.Call(typeof(string), (typeof(string).GetMethod("IsNullOrEmpty")).Name, null, myProperty)
                                                                    let myNotExp = Expression.Not(myNullExp)
                                                                    select new { myExpression, myNotExp })
                {
                    var andAlso = Expression.AndAlso(callContainsMethod.myNotExp, callContainsMethod.myExpression);
                    if (orExpressions == null)
                    {
                        orExpressions = andAlso;
                    }
                    else
                    {
                        orExpressions = Expression.Or(orExpressions, andAlso);
                    }
                }
    
                IQueryable<T> queryable = list.AsQueryable<T>();
                MethodCallExpression whereCallExpression = Expression.Call(
                    typeof(Queryable),
                    "Where",
                    new Type[] { queryable.ElementType },
                    queryable.Expression,
                    Expression.Lambda<Func<T, bool>>(orExpressions, new ParameterExpression[] { parameter }));
                var results = queryable.Provider.CreateQuery<T>(whereCallExpression).ToList();
                return results;
            }
    

    The only part that changed is the LINQ where I get two Expressions which contains one for "Contains" and one for "Not(IsNullOrEmpty)" expression andalso'ed .