C# Dynamic Linq Ternary Operator - c#

I want to make a dynamic check for a null value.
I want to make a where clause which will compare only the date part of the date field.
It will work for non nullable date fields, but for nullable date fields we need to check for value as using .Date on null data will throw an error
let us say
p => (p.Date.Value == null ? null : p.Date.Value.Date) == SelectedDate.Date
or
p => ( p.Date.Value == null ? p.Date.Value : p.Date.Value.Date) == SelectedDate.Date
or
p => (p.Date.Value == null ? p.Date : p.Date.Value.Date) == SelectedDate.Date
basically a null checking ternary operator which selects only the date part of
I already tried
ConstantExpression argument = Expression.Constant(MyDateField, typeof(DateTime));
ParameterExpression parameter = Expression.Parameter(typeof(T), "p");
string field = "Date";
BinaryExpression condition = Expression.Equal(Expression.Property(parameter, field), Expression.Constant(null, typeof(DateTime?)));
ConditionalExpression ternary = Expression.Condition(condition, property, Expression.Property(property, "Date"));
Expression equalExp = Expression.Equal(ternary, argument);
lambda = Expression.Lambda<Func<T, bool>>(equalExp, parameter);
Which gives me
p => (IIF((p.EventDate == null), p.EventDate.Value, p.EventDate.Value.Date) == 21-Jun-18 12:00:00 AM)
but this is not working.
Issue I'm facing is
If I use p.Date.Value in the BinaryExpression then it doesnot allow as .Value makes it DateTime and null is only available in DateTime?
IIF condition is generated and not ?: ternary operator
Any and all help is appreciated.

The DateTime? and DateTime are different types. While the C# compiler does some implicit conversions sometimes (for example when you compare them with ==), with Lambda Expressions you have to make explicit casts. And to get the value of a DateTime? you have to use the .Value property.
public static Expression<Func<T, bool>> MakeExpression<T>(DateTime myDateField)
{
ConstantExpression argument = Expression.Constant(myDateField, typeof(DateTime));
ParameterExpression parameter = Expression.Parameter(typeof(T), "p");
string propertyName = "Date";
Expression property = Expression.Property(parameter, propertyName);
BinaryExpression condition = Expression.Equal(property, Expression.Constant(null, typeof(DateTime?)));
Expression propertyValue = Expression.Property(property, nameof(Nullable<DateTime>.Value));
Expression propertyValueDate = Expression.Property(propertyValue, nameof(DateTime.Date));
ConditionalExpression ternary = Expression.Condition(condition, Expression.Constant(null, typeof(DateTime?)), Expression.Convert(propertyValueDate, typeof(DateTime?)));
Expression argumentDate = Expression.Property(argument, nameof(DateTime.Date));
Expression equalExp = Expression.Equal(ternary, Expression.Convert(argumentDate, typeof(DateTime?)));
var lambda = Expression.Lambda<Func<T, bool>>(equalExp, parameter);
return lambda;
}
Note that Nullable<> defines a HasValue property, instead of comparing the value with null... So you could:
public static Expression<Func<T, bool>> MakeExpression<T>(DateTime myDateField)
{
ConstantExpression argument = Expression.Constant(myDateField, typeof(DateTime));
ParameterExpression parameter = Expression.Parameter(typeof(T), "p");
string propertyName = "Date";
Expression property = Expression.Property(parameter, propertyName);
Expression propertyHasvalue = Expression.Property(property, nameof(Nullable<DateTime>.HasValue));
Expression propertyValue = Expression.Property(property, nameof(Nullable<DateTime>.Value));
Expression propertyValueDate = Expression.Property(propertyValue, nameof(DateTime.Date));
ConditionalExpression ternary = Expression.Condition(Expression.Not(propertyHasvalue), Expression.Constant(null, typeof(DateTime?)), Expression.Convert(propertyValueDate, typeof(DateTime?)));
Expression argumentDate = Expression.Property(argument, nameof(DateTime.Date));
Expression equalExp = Expression.Equal(ternary, Expression.Convert(argumentDate, typeof(DateTime?)));
var lambda = Expression.Lambda<Func<T, bool>>(equalExp, parameter);
return lambda;
}

Let say we have two expressions left and right, where the right type is DateTime, and we want to compare them for equality.
When the left type is DateTime, the comparison is simply
left == right
and when the left type is DateTime?, then
(left == (DateTime?)null ? (DateTime?)null : (DateTime?)left.Value.Date) == (DateTime?)right
I specifically added the required casts. C# compiler does some of them implicitly (like (DateTime?)null), but the important is that the ternary operator result type should be DateTime?, hence both ternary operator operands type and equality operator operands type must be DateTime? as well.
With that being said, let translate the aforementioned rules to code:
static Expression<Func<T, bool>> DateEquals<T>(string memberName, DateTime value)
{
var parameter = Expression.Parameter(typeof(T), "p");
Expression left = Expression.PropertyOrField(parameter, memberName);
Expression right = Expression.Constant(value.Date);
if (left.Type == typeof(DateTime?))
{
var leftValue = Expression.Property(left, "Value");
var nullValue = Expression.Constant(null, typeof(DateTime?));
left = Expression.Condition(
Expression.Equal(left, nullValue),
nullValue,
Expression.Convert(Expression.Property(leftValue, "Date"), typeof(DateTime?))
);
right = Expression.Convert(right, typeof(DateTime?));
}
var condition = Expression.Equal(left, right);
return Expression.Lambda<Func<T, bool>>(condition, parameter);
}
(Don't worry that you see IIF in the debug display. The Conditional expression shown as IIF is indeed the expression equivalent of the C# ? : operator)

I suppose your p.Date is DateTime? (or Nullable<DateTime>)
p => p.Date?.Date == SelectedDate.Date

What ended up working is
public static Expression<Func<T, bool>> MakeExpression<T>(DateTime myDateField, string fieldName)
{
var parameter = Expression.Parameter(typeof(T), "p");
var property = Expression.Property(parameter, fieldName);
var fieldType = property.Type;
Expression<Func<T, bool>> lambda = null;
if (fieldType == typeof(DateTime?))
{
var truncateTimeMethod = typeof(DbFunctions).GetMethod("TruncateTime", new[] { fieldType });
if (truncateTimeMethod != null)
{
var propertyHasvalue = Expression.Property(property, nameof(Nullable<DateTime>.HasValue));
var truncateTimeMethodCall = Expression.Call(truncateTimeMethod, property);
var ternary = Expression.Condition(Expression.Not(propertyHasvalue), property, truncateTimeMethodCall);
var argument = Expression.Constant(myDateField.Date, typeof(DateTime?));
var equalExp = Expression.Equal(ternary, argument);
lambda = Expression.Lambda<Func<T, bool>>(equalExp, parameter);
}
}
return lambda;
}
Thanks to #xanatos
For the .Date functionality
var truncateTimeMethod = typeof(DbFunctions).GetMethod("TruncateTime", new[] { fieldType });
var truncateTimeMethodCall = Expression.Call(truncateTimeMethod, property);
For Ternary Operation
var ternary = Expression.Condition(Expression.Not(propertyHasvalue), property, truncateTimeMethodCall);

Related

Building an Expression that checks if two properties of the object are equal?

I'm trying to understand how Expressions work, so I imagined a method that takes an object that has two int properties and return boolean value indicate if they are equal, something like:
bool AreEqual(Foo foo)
{
return foo.Value1 == foo.Value2;
}
here's the Expression I built:
//build the parameter expression of the object
ParameterExpression parameter = Expression.Parameter(typeof(Foo), "x");
//the left member
MemberExpression leftMember = Expression.Property(parameter, "Value1");
//the right member
MemberExpression rightMember = Expression.Property(parameter, "Value2");
//the left lambda
LambdaExpression leftLmbda = Expression.Lambda(leftMember, parameter);
//the right lambda
LambdaExpression rightLambda = Expression.Lambda(rightMember, parameter);
//and here I evaluate the boolean expression:
Expression equalExpression = Expression.Equal(rightLambda, leftLmbda);
//the lambda of the equal expression
LambdaExpression lambda = Expression.Lambda(equalExpression, parameter);
//the object:
Foo foo = new Foo { Value1= 5, Value2=5 };
Delegate expression = lambda.Compile();
var eq = expression.DynamicInvoke(foo);
but it always evaluates to false.
My guess is that I only build one lambda, but don't know how to handle both properties within one lambda
Your Expression.Equal statment should be comparing the two member expressions.
//build the parameter expression of the object
ParameterExpression parameter = Expression.Parameter(typeof(Foo), "x");
//the left member
MemberExpression leftMember = Expression.Property(parameter, "Value1");
//the right member
MemberExpression rightMember = Expression.Property(parameter, "Value2");
//and here I evaluate the boolean expression:
Expression equalExpression = Expression.Equal(leftMember, rightMember);
//the lambda of the equal expression
LambdaExpression lambda = Expression.Lambda(equalExpression, parameter);
//the object:
Foo foo = new Foo { Value1 = 5, Value2 = 5 };
Delegate expression = lambda.Compile();
var eq = expression.DynamicInvoke(foo);

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.

The parameter '***' was not bound in the specified LINQ to Entities query expression

I am doing a common query in my project. I use Expression to build my query tree, the code list below:
public IList<Book> GetBooksFields(string fieldName, string fieldValue)
{
ParameterExpression paramLeft = Expression.Parameter(typeof(string), "m." + fieldName);
ParameterExpression paramRight = Expression.Parameter(typeof(string), "\"" + fieldValue + "\"");
ParameterExpression binaryLeft = Expression.Parameter(typeof(Book),"m");
BinaryExpression binaryExpr = Expression.Equal(paramLeft, paramRight);
var expr = Expression.Lambda<Func<Book, bool>>(binaryExpr, binaryLeft);
return bookRepository.GetMany(expr).ToList();
}
But when I invoke my GetBooksFields method, it will throw me an exception as below:
I debugged the expr variable and got the correct expression: {m => (m.Name == "sdf")}, it was what I want, But I don't know why I got the error,thx.
You can't "trick" LINQ into interpreting parameters as member-expressions by throwing in dots into variable names.
You'll have to construct the expression-tree correctly, as below
(EDIT: changed field to property as per your comment):
public IList<Book> GetBooksFields(string propertyName, string propertyValue)
{
var parameter = Expression.Parameter(typeof(Book), "book");
var left = Expression.Property(parameter, propertyName);
var convertedValue = Convert.ChangeType
(
propertyValue,
typeof(Book).GetProperty(propertyName).PropertyType
);
var right = Expression.Constant(convertedValue);
var binaryExpr = Expression.Equal(left, right);
var expr = Expression.Lambda<Func<Book, bool>>(binaryExpr, parameter);
return bookRepository.GetMany(expr).ToList();
}

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);

Expression predicates with several parameters

I use the code gave on Stackoverflow by Marc Gravell here :
http://goo.gl/57nW2
The code :
var param = Expression.Parameter(typeof (Foo));
var pred = Expression.Lambda<Func<Foo, bool>>(
Expression.Call(
Expression.PropertyOrField(param, fieldName),
"StartsWith",null,
Expression.Constant(stringToSearch)), param);
Now, I'd like combine several argument, sample :
public void BuildPredicate(string[] typeSearch, string[] field, string searchValue)
{
//Content
//typeSearch = new string[] {"Contains", "StartsWith", "StartsWith" };
//field = new string[] { "FieldA", "FieldB", "FieldC" };
//FieldA contains searchValue and FieldB startWith searchValue and FieldC startWith searchValue
}
An idea ?
Thanks,
You can simply loop over all operations on all fields and build up a Expression tree containing an OrElse clause for each type/field combination
var expressions = from type in typeSearch
from field in fields
select Expression.Call(
Expression.PropertyOrField(param, field),
type, null,
Expression.Constant(stringToSearch));
Expression body = Expression.Constant(false);
foreach (Expression expression in expressions)
{
body = Expression.OrElse(body, expression);
}
var result = Expression.Lambda<Func<Foo, bool>>(body, param);
And as requested, an example including calls to ToUpper:
var expressions = from type in typeSearch
from field in fields
select Expression.Call(
Expression.Call(
Expression.PropertyOrField(param, field),
"ToUpper", null),
type, null,
Expression.Constant(stringToSearch));

Categories

Resources