Enigma Neo
Enigma Neo

Reputation: 35

Dealing with nulls indynamically created linq expression

I am dynamically creating a Linq expression based on string arrays and I'm running into a problem. The way that the expression is created and parenthesized it is causing this to throw an object null reference on Id 3. It creates this expression and If it were parenthesized correctly it wouldn't evaluate the second half of the expression and wouldn't throw an error I assume. Anyone have a way of creating the expression so it doesn't end up parenthesized like this?

{x => ((True And x.Id.ToString().ToLower().Contains("John")) Or ((x.Name != null) And     x.Name.ToString().ToLower().Contains("John")))}

class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Test
{
    public void someMethod()
    {
        var x = new List<Person>(new Person[] { 
            new Person { Id = 1, Name = "Jerry" },
            new Person { Id = 2, Name = "Mary" },
            new Person { Id = 3, Name = null },
            new Person { Id = 4, Name = "John" },
            new Person { Id = 5, Name = "Amy" }
        });

        var columns = new List<string>(new string[] {
            "Name",
            "Id"
        });
        var searchTerm = "John";

        var searchColumns = columns.Select(a => new { ColName = a });


        var type = typeof(Person);

        ParameterExpression paramExpr = Expression.Parameter(type, "x");
        Expression body = null;

        var piList = new List<PropertyInfo>();

        foreach (var s in searchColumns)
            piList.Add(type.GetProperty(s.ColName));

        if (piList[0].PropertyType.IsPrimitive || piList[0].PropertyType.Equals(typeof(DateTime)))
            body = Expression.Constant(true);
        else
            body = Expression.NotEqual(Expression.Property(paramExpr, piList[0]), Expression.Constant(null, piList[0].PropertyType));

        body = Expression.And(body,
                    Expression.Call(
                            Expression.Call(
                                Expression.Call(
                                    Expression.Property(paramExpr, piList[0]),
                                    typeof(Convert).GetMethod("ToString", Type.EmptyTypes)
                                ),
                                typeof(string).GetMethod("ToLower", new Type[0])
                            ),
                            typeof(string).GetMethod("Contains"),
                            Expression.Constant(searchTerm.ToLower())
                        ));

        for (int i = 1; i < piList.Count; i++)
        {
            Expression body1 = null;

            if (piList[i].PropertyType.IsPrimitive || piList[i].PropertyType.Equals(typeof(DateTime)))
                body1 = Expression.Constant(true);
            else
                body1 = Expression.NotEqual(Expression.Property(paramExpr, piList[i]), Expression.Constant(null, piList[i].PropertyType));

            body =
                Expression.Or(body,
                    Expression.And(body1,
                        Expression.Call(
                            Expression.Call(
                                Expression.Call(
                                    Expression.Property(paramExpr, piList[i]),
                                    typeof(Convert).GetMethod("ToString", Type.EmptyTypes)
                                ),
                                typeof(string).GetMethod("ToLower", new Type[0])
                            ),
                            typeof(string).GetMethod("Contains"),
                            Expression.Constant(searchTerm.ToLower())
                        )
                    ));
        }

        var lambda = Expression.Lambda<Func<Person, bool>>(body, paramExpr);
    }
}

Upvotes: 1

Views: 360

Answers (1)

tvanfosson
tvanfosson

Reputation: 532465

Why not just build one of these. They both avoid the issue with null values for Name.

(x.Name ?? "").IndexOf("John", StringComparison.CurrentCultureIgnoreCase) >= 0

or, if you really want equality, not contains it as a substring

string.Equals(x.Name, "John", StringComparison.CurrentCultureIgnoreCase)

BTW - x.Id will never contain "John" and neither will lowercase strings.

Also, you might want to consider using a PredicateBuilder instead of building the expression directly.

Upvotes: 1

Related Questions