How to implement a Linq NullCheck Expression? - c#

I'm having a hard time trying to implement a nullcheck (isNull, isNotNull) expression using linq.
Here is part of my code:
//create a blog collection
var blogsCollection = new List<Blog>();
for (var i = 1; i <= 15; i++)
{
var b = new Blog()
{
BlogId = i,
Url = $"http://{i}.blog.com",
CreatedAt = DateTime.UtcNow.AddDays(-i),
};
blogsCollection.Add(b);
}
ParameterExpression parameterExpression = Expression.Parameter(typeof(Blog), "item");
Expression propertyExpression = Expression.Property(parameterExpression, "BlogId");
Expression targetExpression = Expression.Constant(null, propertyExpression.Type);
Expression exp = Expression.Equal(propertyExpression, targetExpression);
var filter=Expression.Lambda<Func<Blog, bool>>(exp, parameterExpression).Compile();
var blogs = (List<Blog>)blogsCollection.Where(filter);
This code throws an exception:
Argument types do not match
If I change the targetExpression to (without the type conversion):
Expression targetExpression = Expression.Constant(null);
Then it throws a different exception:
System.InvalidOperationException : The binary operator Equal is not defined for the types 'System.Int32' and 'System.Object'
Any Ideas about how to have a null check in Linq? I've checked here on SO, but none of them seems to work.

Your Id is of type System.Int32 which is a value type, so it is always not null (i.e. BlogId == null is always false). If you want to implement a generic filter which supports every possible input you can do something like that:
if (propertyExpression.Type.IsValueType && Nullable.GetUnderlyingType(propertyExpression.Type) == null)
{
filter = Expression.Lambda<Func<Blog, bool>>(Expression.Constant(false), parameterExpression).Compile();
}
else
{
// your current code
}

Related

Expression trees in middle of regular LINQ method chaining; dynamic and nullable types?

I'm trying to write a dynamic query but having trouble when dealing with dynamic types in the OrderBy section of my query. I've seen some solutions using method call expressions, which may work for me, but I don't know how to fit that into my existing code where I am using method chaining on either side. It's possible I can just use dynamic as my type but I don't think that will work with Nullable types.
public async Task<IEnumerable<MyEntityModel>> GetEntities(QueryEntityResource request)
{
IQueryable<MyEntityModel> queryableData = ...;
Expression whereLambdaExp = ...;
queryableData = queryableData.Where(whereLambdaExp);
ParameterExpression param = Expression.Parameter(typeof(MyEntityModel));
PropertyInfo property = typeof(MyEntityModel).GetProperty(request.sortModel.ColId);
Expression propExpr = Expression.Property(param, property);
var lambdaExpression = Expression.Lambda<Func<MyEntityModel, dynamic>>(propExpr , new ParameterExpression[] { param }); // can't use dynamic
// Errors here; can't use dynamic type in lambda expression -- Need to specify types
if (request.sortModel.Sort == "asc")
{
queryableData = queryableData.OrderBy(lambdaExpression);
}
if (request.sortModel.Sort == "desc")
{
queryableData = queryableData.OrderByDescending(lambdaExpression);
}
queryableData = queryableData.Skip(request.StartRow).Take(request.EndRow - request.StartRow);
return queryableData.ToListAsync();
}
If I use dynamic it won't work with my nullable types:
System.ArgumentException: Expression of type 'System.Nullable`1[System.Int32]' cannot be used for return type 'System.Object'
This is untested; but I think it is close to what you need (I feel like I've written this code before... also, see this answer):
You can build calls to OrderBy instantiated to the types you need and then build a new IQueryable. My method removes the async as I couldn't build a quick test for that in LINQPad.
public IEnumerable<MyEntityModel> GetEntities(QueryEntityResource request) {
IQueryable<MyEntityModel> queryableData = default;
Expression<Func<MyEntityModel, bool>> whereLambdaExp = default;
queryableData = queryableData.Where(whereLambdaExp);
var param = Expression.Parameter(typeof(MyEntityModel));
var propExpr = Expression.Property(param, request.sortModel.ColId);
var lambdaExpression = Expression.Lambda(propExpr, param);
if (request.sortModel.Sort == "asc" || request.sortModel.Sort == "desc")
queryableData = queryableData.Provider.CreateQuery<MyEntityModel>(Expression.Call(typeof(Queryable), request.sortModel.Sort == "asc" ? "OrderBy" : "OrderByDescending",
new[] { typeof(MyEntityModel), propExpr.Type }, queryableData.Expression, Expression.Quote(lambdaExpression)));
queryableData = queryableData.Skip(request.StartRow).Take(request.EndRow - request.StartRow);
return queryableData.ToList();
}

Prepare Dynamic Lambda Expression with Checking parentproperty null control

I have dynamic lambda expression tree,I prepare my expression and compile it.
My problem is when I prepare expression something like
MyClass1.Property2.ChildProperty1=="xyz";
if Property2 is null then I take nullrefence error.
public class MyClass1
{
public string Property1{get;set;}
public MyClass2 Property2{get;set;}
}
public class MyClass2
{
public string ChildProperty1{get;set;}
public string ChildProperty2{get;set;}
}
public Func<T, bool> CompileRule<T>(Rule r)
{
...
Func<T, bool> myfunc =Expression.Lambda<Func<T, bool>>(expr, paramUser).Compile();
...
return myfunc;
}
Another function I execute my request like " myfunc(myrequest); "
Is there a generic way to check null control when I preprare my expression in CompileTime,If ParentProperty null then return false for these statement?In compile method I dont have entity ,so I have to prepare statement with check null control
So you want to create dynamically a body of an expression like this:
Expression<Func<MyClass1, string>> expr =
x => x.Property2?.ChildProperty1;
Unfortunately currently there is not standard Expression method supporting that. If you try the above, you'll get a compile time error:
Error CS8072 An expression tree lambda may not contain a null propagating operator.
One possible way is to generate a body of an expression like this instead:
Expression<Func<MyClass1, string>> expr =
x => x.Property2 != null ? x.Property2.ChildProperty1 : null;
It's not the exact equivalent, but should work.
Here is a sample snippet which does that dynamically:
string memberPath = ...; // e.g. "Property2.ChildProperty2"
var parameter = Expression.Parameter(typeof(T), "x");
Expression result;
if (memberPath.IndexOf('.') < 0)
result = Expression.PropertyOrField(parameter, memberPath);
else
{
var memberNames = memberPath.Split('.');
var members = new Expression[memberNames.Length];
for (int i = 0; i < memberNames.Length; i++)
members[i] = Expression.PropertyOrField(i > 0 ? members[i - 1] : parameter, memberNames[i]);
result = members[members.Length - 1];
// For non nullable value types, promote the result into the corresponding nullable type
if (result.Type.IsValueType && Nullable.GetUnderlyingType(result.Type) == null)
result = Expression.Convert(result, typeof(Nullable<>).MakeGenericType(result.Type));
var nullResult = Expression.Constant(null, result.Type);
for (int i = members.Length - 2; i >= 0; i--)
{
result = Expression.Condition(
Expression.NotEqual(members[i], Expression.Constant(null)),
result, nullResult);
}
}
At the end, the result variable contains the expression that you can use for left side of the comparison predicate.

LINQ to Entities - Method not recognized

I'm facing a problem with executing a query against a Queryable dataset.
The original call looks like this:
books = books.Where(b => (GetPropertyValue(b, filter.CategoryProperties.DbName) == null ? 0 : Convert.ToInt32(GetPropertyValue(b, filter.CategoryProperties.DbName))) < Convert.ToInt32(filter.Value));
This gets me the Method not recognized error.
This of course is expected due to the call to GetPropertyValue. I then read that I should build the expression tree myself.
That result in the following code:
public IQueryable<Books> GetExpression(IQueryable<Books> books, BookCategoryMapping filter)
{
var booksExpression = Expression.Parameter(typeof(Books), "b");
var methodInfo = this.GetType().GetMethod("GetPropertyValue");
var value = Expression.Call(methodInfo, booksExpression, Expression.Constant(filter.CategoryProperties.DbName));
var left = Expression.Constant(value);
var right = Expression.Constant(filter.Value);
var expression = Expression.Equal(left, right);
var whereExpression = Expression.Call(typeof(Queryable), "Where", new Type[] { books.ElementType }, books.Expression, Expression.Lambda<Func<Books, bool>>(expression, new ParameterExpression[] { booksExpression }));
return books.Provider.CreateQuery<Books>(whereExpression);
}
Only problem being, that I get the same error. It seems like the following line only produces an expression and not the value of said expression.
var value = Expression.Call(methodInfo, booksExpression, Expression.Constant(filter.CategoryProperties.DbName));
Any help producing the correct expression tree would be greatly appreciated :-)
EDIT:
Here's the GetPropertyValue method:
public static object GetPropertyValue(object obj, string name)
{
try
{
return obj.GetType().GetProperty(name)?.GetValue(obj, null);
}
catch (Exception ex)
{
LogManager.Log(LogLevel.Error, null, ex);
}
return obj;
}
The method below generates an Expression<Func<Book, bool>> which determines whether a given property of a Book is less than a given (constant) value. The error-checking code you currently have in GetPropertyValue can probably be replaced by catching the ArgumentException which will be thrown when you try to create an expression for a nonexistent property.
Note that I have assumed that the property you're accessing is genuinely numeric, and that your call to Convert.ToInt32 was only necessary because your GetPropertyValue method returns an object.
Expression<Func<Book, bool>> GenerateLessThanExpression(string propertyName, int value)
{
var parameter = Expression.Parameter(typeof (Book));
var property = Expression.Property(parameter, propertyName);
var comparison = Expression.LessThan(property, Expression.Constant(value));
return Expression.Lambda<Func<Book, bool>>(comparison, parameter);
}
Here's a sample usage to return only very short books:
var filter = GenerateLessThanExpression("Pages", 5);
var filtered = books.Where(filter);

Nullable comparisons in expression trees

I'm trying to build a dynamic LINQ query, in Entity Framework, from a user-provided set of collection criteria. Eventually, this will include more complex behaviors, but currently I just have a list of field names and values, and I want to return all the records in which the field names have those values.
My basic structure is this:
public IEnumerable<ThingViewModel> getMythings(SelectionCriteria selectionCriteria)
{
var predicate = constructPredicate<Thing>(selectionCriteria);
var things = this.dbContext.Things.Where(predicate).ToList();
return Mapper.Map<List<Thing>, List<ThingViewModel>>(things);
}
Where all the interesting work is in constructPredicate():
private static Expression<Func<T, bool>> constructPredicate<T>(SelectionCriteria selectionCriteria)
{
// using Pete Montgomery's PredicateBuilder:
// http://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/
var predicate = PredicateBuilder.True<T>();
foreach (var item in selectionCriteria.andList)
{
// Accessing foreach values in closures can result in unexpected results.
// http://stackoverflow.com/questions/14907987/access-to-foreach-variable-in-closure
var fieldName = item.fieldName;
var fieldValue = item.fieldValue;
var parameter = Expression.Parameter(typeof (T), "t");
var property = Expression.Property(parameter, fieldName);
var value = Expression.Constant(fieldValue);
var lambda = buildCompareLambda<T>(property, value, parameter);
predicate = predicate.And(lambda);
}
return predicate;
}
All of that seems like a perfectly reasonable structure, but it's in the buildCompareLambda() that I'm having difficulties. I'm not seeing a way to do this in a generic way, I'm having to create different methods for different types. I'd started by handling strings, and that was simple enough. I next tried to handle integers, but it turns out that the integer fields in the database are nullable, which introduced a whole new class of complexity.
My buildCompareLambda(), so far:
private static Expression<Func<T, bool>> buildCompareLambda<T>(
MemberExpression property,
ConstantExpression value,
ParameterExpression parameter)
{
Expression<Func<T, bool>> lambda = null;
if (property.Type == typeof (string))
lambda = buildStringCompareLambda<T>(property, value, parameter);
else if (property.Type.IsGenericType && Nullable.GetUnderlyingType(property.Type) != null)
lambda = buildNullableCompareLambda<T>(property, value, parameter);
if (lambda == null)
throw new Exception(String.Format("SelectrionCriteria cannot handle property type '{0}'", property.Type.Name));
return lambda;
}
As I said, buildStringCompareLambda is simple enough:
private static Expression<Func<T, bool>> buildStringCompareLambda<T>(
MemberExpression property,
ConstantExpression value,
ParameterExpression parameter)
{
var equalsMethod = typeof (string).GetMethod("Equals",
new[] {typeof (string), typeof (string)});
var comparison = Expression.Call(equalsMethod, property, value);
return Expression.Lambda<Func<T, bool>>(comparison, parameter);
}
But buildNullableCompareLambda() is getting ugly:
private static Expression<Func<T, bool>> buildNullableCompareLambda<T>(MemberExpression property,
ConstantExpression value,
ParameterExpression parameter)
{
var underlyingType = Nullable.GetUnderlyingType(property.Type);
if (underlyingType == typeof (int) || underlyingType == typeof (Int16) || underlyingType == typeof (Int32) ||
underlyingType == typeof (Int64) || underlyingType == typeof (UInt16) || underlyingType == typeof (UInt32) ||
underlyingType == typeof (UInt64))
{
var equalsMethod = underlyingType.GetMethod("Equals", new[] {underlyingType});
var left = Expression.Convert(property, underlyingType);
var right = Expression.Convert(value, underlyingType);
var comparison = Expression.Call(left, equalsMethod, new Expression[] {right});
return Expression.Lambda<Func<T, bool>>(comparison, parameter);
}
return null;
}
It's my intent to add support for more types, in buildNullableCompareLambda(), and to move the handling of each type into a function, so that the same code can be called from buildCompareLambda() and from buildNullableCompareLambda(). But that's for the future - currently I'm stuck on comparing ints. Currently, I'm converting both the property and the value to the underlying type, since I don't want to have separate functions for each integer type, and I don't want the user to have to care whether the EF models a field into an Int16 or an Int32. And that's working, for non-null fields.
I've been browsing around SO, and finding some answers, which is how I've gotten as far as I have, but none of the answers I've seen on handling nullable types really work for me, because they don't really handle nulls.
In my case, if the user passes me a selection criteria with an item that is supposed to equal null, I would want to return the records for which that field are null, and this bit about converting both sides to a base type doesn't seem to work. I'm getting an "Object reference not set to an instance of an object" exception.
In SQL, what I'd want is a "WHERE field IS NULL", if the value is null, or "WHERE field = 'value'", if it is not. And I'm not seeing how to build that kind of alternative into an expression tree.
Any ideas?
Added: It has been suggested that I use Expression.Equal().
With that, my loop becomes:
private static Expression<Func<T, bool>> constructPredicate<T>(SelectionCriteria selectionCriteria)
{
var predicate = PredicateBuilderEx.True<T>();
var foo = PredicateBuilder.True<T>();
foreach (var item in selectionCriteria.andList)
{
var fieldName = item.fieldName;
var fieldValue = item.fieldValue;
var parameter = Expression.Parameter(typeof (T), "t");
var property = Expression.Property(parameter, fieldName);
var value = Expression.Constant(fieldValue);
var comparison = Expression.Equal(property, value);
var lambda = Expression.Lambda<Func<T, bool>>(comparison, parameter);
predicate = PredicateBuilderEx.And(predicate, lambda);
}
return predicate;
}
And that doesn't work. I get an exception:
The binary operator Equal is not defined for the types
'System.Nullable`1[System.Int16]' and 'System.Int16'.
As is quite often the case, folks here may not quite come up with the answer, but they get most of the way, and close enough that I can work out the rest.
Expression.Equal requires both parameters to be of the same type. If one is nullable, they both need to be nullable. But that's not that hard to deal with:
private static Expression<Func<T, bool>> constructPredicate<T>(SelectionCriteria selectionCriteria)
{
var predicate = PredicateBuilderEx.True<T>();
var foo = PredicateBuilder.True<T>();
foreach (var item in selectionCriteria.andList)
{
var fieldName = item.fieldName;
var fieldValue = item.fieldValue;
var parameter = Expression.Parameter(typeof (T), "t");
var property = Expression.Property(parameter, fieldName);
var value = Expression.Constant(fieldValue);
var converted = Expression.Convert(value, property.Type);
var comparison = Expression.Equal(property, converted);
var lambda = Expression.Lambda<Func<T, bool>>(comparison, parameter);
predicate = PredicateBuilderEx.And(predicate, lambda);
}
return predicate;
}
Thanks, all.
As Lee states in his comment, you don't need to implement buildNullableCompareLambda<T> for each type. There is already a method that checks the types of the left and right expressions and calls the Equals method on them if they are a user-defined type, and does the lifting and proper comparison if they are nullable types. See here.
Your lambda is basically:
var property = Expression.Property(parameter, fieldName);
var value = Expression.Constant(fieldValue);
var lambda = Expression.Equal(property, value);
Edit:
It seems to me that this is a bug. Eric Lippert thinks so(link). The documentation states the scenario where they are both of the same type and what to do then:
If left.Type and right.Type are both non-nullable, the node is not
lifted. The type of the node is Boolean. If left.Type and right.Type
are both nullable, the node is lifted. The type of the node is
Boolean.
It doesn't exactly what would happen if one is nullable and the other isn't. In the same link referenced, Eric gives a workaround.

Expression.Equal - How to Compare Nullable and Non Nullable fields?

I have a nullable datetime field and I have to convert the string date field to a nullable datetime type (using Expression)....I did this using the below.
Expression.Constant(Convert.ChangeType(value, Nullable.GetUnderlyingType(memberAccess.Type)));.
The memberAccess (mentioned above) is of type member expression. (From LinqExtensions.cs)
Now In the code I am using Expression.Equal Method.
Expression.Equal(memberAccess, filter);
This fails here, as memberaccess type is nullable but filter.type is not nullable...
Even if I try to convert the member access type to nullable using
ConstantExpression test = Expression.Constant(Nullable.GetUnderlyingType(memberAccess.Type)),
the Type is Runtime and not DateTime.
How to use Expression.Equal to compare nullable & non nullable field? IS there any way to convert the string type to a nullable datetime field? Either one of this will resolve my issue.
Ok..I did this way.
First Converted the type (string to datetime)
filter = Expression.Constant(
Convert.ChangeType(value, memberAccess.Type.GetGenericArguments()[0]));
then converted this expression to the desired type
Expression typeFilter = Expression.Convert(filter, memberAccess.Type);
Then used Expression.Equal(memberAccess, typeFilter)...
(memberAccess is MemberExpression and it takes the property type from model)
If you have values Nullable values other than dates, This is how You can create an Expression tree for nullable types, suppose you have a nullable field BoardId, you can create expression tree dynamically like this
var nameValue="BoardId=111";
public static Expression<Func<T, bool>> BuildWhereExpression<T>(string nameValueQuery ) where T : class
{
Expression<Func<T, bool>> predicate = null;
PropertyInfo prop = null;
var fieldName = nameValueQuery.Split("=")[0];
var fieldValue = nameValueQuery.Split("=")[1];
var properties = typeof(T).GetProperties();
foreach (var property in properties)
{
if (property.Name.ToLower() == fieldName.ToLower())
{
prop = property;
}
}
if (prop != null)
{
var isNullable = prop.PropertyType.IsNullableType();
var parameter = Expression.Parameter(typeof(T), "x");
var member = Expression.Property(parameter, fieldName);
if (isNullable)
{
var filter1 =
Expression.Constant(
Convert.ChangeType(fieldValue, member.Type.GetGenericArguments()[0]));
Expression typeFilter = Expression.Convert(filter1, member.Type);
var body = Expression.Equal(member, typeFilter);
predicate = Expression.Lambda<Func<T, bool>>(body, parameter);
}
else
{
if (prop.PropertyType == typeof(string) && likeOerator.ToLower() == "like")
{
var parameterExp = Expression.Parameter(typeof(T), "type");
var propertyExp = Expression.Property(parameterExp, prop);
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var someValue = Expression.Constant(fieldValue, typeof(string));
var containsMethodExp = Expression.Call(propertyExp, method, someValue);
predicate = Expression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp);
}
else
{
var constant = Expression.Constant(Convert.ChangeType(fieldValue, prop.PropertyType));
var body = Expression.Equal(member, constant);
predicate = Expression.Lambda<Func<T, bool>>(body, parameter); `enter code here`
}
}
}
return predicate;
}
1- This Solution first checks for the Nullable value and generate the expression.
This is How you can determine if the type is Nullable. I have created an extension method for that purpose
public static bool IsNullableType(this Type type)
{
return type.IsGenericType && (type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)));
}
2- the second step is to check the type if its string then create an expression for a string.
3- the Third step is to check is value is not nullable not string then create an expression using equal
You should be using DateTime.Parse or, better, DateTime.ParseExact to convert your string to date. So, Expression.Call.
If you want to convert it to null date if the string is null, you can use Expression.Condition.
If you want to make the result nullable, I believe Expression.Convert can do that.
So something like (pseudocode):
Condition(
Equals(yourStringExpression, null),
Constant(null, typeof(DateTime?)),
Convert(
Call(DateTime.ParseExact, yourStringExpression, ...),
typeof(DateTime?)
)
)
You can pass Type in Expression.Constant:
ConstantExpression constant = Expression.Constant(value, member.Type);
BinaryExpression equalExpression = Expression.Equal(member, constant);

Categories

Resources