Creating dynamic Expression<Func<T,Y>> - c#

I want to create a dynamic Expression<Func<T,Y>>. Here is the code which works for string but doesn't work for DateTime. By doesn't work I mean, I get this exception:
"Expression of type 'System.Nullable`1[System.DateTime]' cannot be
used for return type 'System.Object'"
Can anybody analyze the mistake?
Type type = typeof(DSVPNProjection);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
PropertyInfo propertyInfo = type.GetProperty(sidx);
expr = Expression.Property(expr, propertyInfo);
var expression =
Expression.Lambda<Func<DSVPNProjection, object>>(expr, arg);
Do I need to change the object to some other type? If yes, then which? As you can see I am trying to dynamically fetch the PropertyInfo and use that as the 2nd parameter in Func.

For value types, you need to perform the boxing explicitly (i.e. convert to Object):
Type type = typeof(DSVPNProjection);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = null;
PropertyInfo propertyInfo = type.GetProperty(sidx);
expr = Expression.Property(arg, propertyInfo);
if (propertyInfo.PropertyType.IsValueType)
expr = Expression.Convert(expr, typeof(object));
var expression =
Expression.Lambda<Func<DSVPNProjection, object>>(expr, arg);

Related

Linq expression to call Contains method with nullable parameter

I have database column
SelectListId int null
And I have complicated filter on web page, which I am trying to transfer to LinqToSql in order to get filtered data from database.
I have several expressions which works, but one I struggle with.
I would like to call something like this x => SelectedIdsByUser.Contains(x.SelectListId)
So I have a function which returns predicate
// this function works for 'SelectListId int not null' columns
public static Expression<Func<T, bool>> SelectListContainsPredicate<T>(string columnName, List<int> searchValues)
{
var type = typeof(T);
ParameterExpression parameter = Expression.Parameter(type, "x");
ConstantExpression constant = Expression.Constant(true);
// When column is invalid, return true
PropertyInfo property = type.GetProperties().FirstOrDefault(p => p.Name == columnName);
if (property == null || searchValues.Count == 0)
{
return Expression.Lambda<Func<T, bool>>(constant, parameter);
}
// Define expression :
// x => SearchValues.Contains(x.Column)
MemberExpression member = Expression.Property(parameter, property);
MethodInfo method = typeof(List<int>).GetMethod("Contains");
constant = Expression.Constant(searchValues);
// Here it throws :
// System.ArgumentException: Expression of type 'System.Nullable`1[System.Int32]'
// cannot be used for parameter of type 'System.Int32' of method 'Boolean Contains(Int32)'
// Because: column is int? and List<int>.Contains(int?) doesn't work.
Expression expression = Expression.Call(constant, method, member);
return Expression.Lambda<Func<T, bool>>(expression, parameter);
}
but I am getting an error, because SelectListId is Nullable<int> but Contains method has only int parameter. What can I do here, any Idea ?
System.ArgumentException: Expression of type
'System.Nullable`1[System.Int32]' cannot be used for parameter of
type 'System.Int32' of method 'Boolean Contains(Int32)'
use
x => ( x.SelectListId != null ) ? SelectedIdsByUser.Contains( (int)(x.SelectListId) ) : 0
and replace 0 by intendent value.
I found solution here
https://learn.microsoft.com/en-us/dotnet/api/system.linq.expressions.expression.convert?view=netframework-4.8
there is Expression.Convert method, I used :-)
Expression memberAsInt = Expression.Convert(member, typeof(Int32));
Expression expression = Expression.Call(constant, method, memberAsInt);
return Expression.Lambda<Func<T, bool>>(expression, parameter);

Creating a constant Expression in C#

Is there a way to create an expression in C#, that returns a constant value, but also has a parameter?
Using code, it would look like
var expression = x => 5
So it should match Expression<Func<double, double>> type.
It probably should look like
ParameterExpression param = Expression.Parameter(typeof(double), "parameter");
ConstantExpression constant = Expression.Constant(0.0);
var expression = Expression.SomeMagic(param, constant);
Expression.Lambda<Func<double, double>>(expression);
You pass your parameters into Expression.Lambda<>() :
https://msdn.microsoft.com/en-us/library/dd268052(v=vs.110).aspx
So you're looking for something like :
ParameterExpression param = Expression.Parameter(typeof(double), "parameter");
ConstantExpression constant = Expression.Constant(0.0);
var expression = // build the body ...
Expression.Lambda<Func<double, double>>(expression, param);
To return the same parameter, it'd be as simple as :
ParameterExpression param = Expression.Parameter(typeof(double), "parameter");
Expression.Lambda<Func<double, double>>(param, param);
To return the constant value would be :
ParameterExpression param = Expression.Parameter(typeof(double), "parameter");
ConstantExpression constant = Expression.Constant(0.0);
Expression.Lambda<Func<double, double>>(constant, param);

Dynamic lambda expression for array property filter

As per requirement I want to create a dynamic lambda expression using C#.
For example I want to generate the dynamic query like
Employee. Address[1].City
How can I do this? Please note that the property is a dynamic one.
I have tried this code
var item = Expression.Parameter(typeof(Employee), "item");
Expression prop = Expression.Property(item, "Address", new Expression[] { Expression.Constant[1] });
prop = Expression.Property(prop, "City");
var propValue = Expression.Constant(constraintItem.State);
var expression = Expression.Equal(prop, propValue);
var lambda = Expression.Lambda<Func<Line, bool>>(expression, item);
But it did not work.
Any help would be appreciated.
Thanks.
You "dynamic query" expression (which is not really a query, it's a simple MemberExpression) can be produced as follows:
ParameterExpression param = Expression.Parameter(typeof(Employee), "item");
MemberExpression address = Expression.Property(param, "Address");
BinaryExpression indexedAddress = Expression.ArrayIndex(address, Expression.Constant(1));
MemberExpression city = Expression.Property(indexedAddress, "City"); // Assuming "City" is a string.
// This will give us: item => item.Address[1].City
Expression<Func<Employee, string>> memberAccessLambda = Expression.Lambda<Func<Employee, string>>(city, param);
If you want an actual predicate to use as part of your query, you just wrap the MemberExpression with a relevant compare expression, i.e.
BinaryExpression eq = Expression.Equal(city, Expression.Constant("New York"));
// This will give us: item => item.Address[1].City == "New York"
Expression<Func<Employee, bool>> predicateLambda = Expression.Lambda<Func<Employee, bool>>(eq, param);
In terms of your code: not sure why you're creating a lambda where the delegate type is Func<Line, bool> when the input is clearly expected to be Employee. Parameter type must always match the delegate signature.
EDIT
Non-array indexer access example:
ParameterExpression param = Expression.Parameter(typeof(Employee), "item");
MemberExpression address = Expression.Property(param, "Address");
IndexExpression indexedAddress = Expression.MakeIndex(
address,
indexer: typeof(List<string>).GetProperty("Item", returnType: typeof(string), types: new[] { typeof(int) }),
arguments: new[] { Expression.Constant(1) }
);
// Produces item => item.Address[1].
Expression<Func<Employee, string>> lambda = Expression.Lambda<Func<Employee, string>>(indexedAddress, param);
// Predicate (item => item.Address[1] == "Place"):
BinaryExpression eq = Expression.Equal(indexedAddress, Expression.Constant("Place"));
Expression<Func<Employee, bool>> predicateLambda = Expression.Lambda<Func<Employee, bool>>(eq, param);

Extend the Where method for IQueryable [duplicate]

This question already has an answer here:
extend Where for IQueryable
(1 answer)
Closed 8 years ago.
I want to extend the Where to something like this: .WhereEqual("Field", "SomeString") wich must be equivalent to .Where(p => p.Field== "SomeString"), here is my code:
public static IQueryable<T> WhereEqual<T>(this IQueryable<T> q, string Field, string Value)
{
var param = Expression.Parameter(typeof(T), "p");
var prop = Expression.Property(param, Field);
var val = Expression.Constant(Value);
var body = Expression.Equal(prop, val);
var exp = Expression.Lambda<Func<T, bool>>(body, param);
Type[] types = new Type[] { q.ElementType, typeof(bool) };
var mce = Expression.Call(
typeof(Queryable),
"Where",
types,
exp
);
return q.Provider.CreateQuery<T>(mce);
}
thanks to the debugger i can see that exp is set to {p => (p.Field== "SomeString")}, but the Call method throws the following exception :
"An exception of type 'System.InvalidOperationException' occurred in
System.Core.dll but was not handled in user code, Additional
information: No generic method 'Where' on type 'System.Linq.Queryable'
is compatible with the supplied type arguments and arguments. No type
arguments should be provided if the method is non-generic."
How can I give the Where the needed arguments ?
Thanks.
public static IQueryable<T> WhereEqual<T>(this IQueryable<T> q, string Field, string Value)
{
var param = Expression.Parameter(typeof(T), "p");
var prop = Expression.Property(param, Field);
var val = Expression.Constant(Value);
var body = Expression.Equal(prop, val);
var exp = Expression.Lambda<Func<T, bool>>(body, param);
return System.Linq.Queryable.Where(q, exp);
}

Having problems with scope of lambda expressions

Let's use this method that I'm trying to call as an example:
MyMethod<MyType, string>(Expression<Func<MyType, string>> expression)
If I try to dynamically build the following expression x => x.Name and pass it to the method, everything works just fine:
var pi = typeof(MyType).GetProperty("Name");
ParameterExpression pe = Expression.Parameter(typeof(MyType), "x");
MemberExpression me = Expression.Property(pe, pi);
var exp = Expression.Lambda(me, pe);
Now let's imagine that in the exact same scope, I'm trying to build this expression instead x => item.Name using the following code, obviously item being different than the input parameter x:
var item = new MyType() { Name = "My name" };
var pi = t.GetProperty("Name");
ParameterExpression pe = Expression.Parameter(typeof(MyType), "x");
ParameterExpression pe2 = Expression.Parameter(typeof(MyType), "item");
MemberExpression me = Expression.Property(pe2, pi);
var exp = Expression.Lambda(me, pe);
When I try to call MyMethod I get the following error, probably because there's no variable "item" in the scope where MyMethod is called (which is not where I build the expression):
variable 'item' of type 'MyType' referenced from scope '', but it is not defined
How can I build this expression in such a way that it won't throw this error?
Within the scope of the lambda, the item bit is a constant - it's not a variable parameter to the invocation.
So the following should be closer:
var item = new MyType() { Name = "My name" };
var pi = t.GetProperty("Name");
ParameterExpression pe = Expression.Parameter(typeof(MyType), "x");
ConstantExpression ce = Expression.Constant(typeof(MyType), item);
MemberExpression me = Expression.Property(ce, pi);
var exp = Expression.Lambda(me, pe);
The string "item" is not related in any way to the local variable item. If you want your constructed lambda expression to capture the value of the local variable item, then instead of Expression.Parameter(typeof(MyType), "item") you need Expression.Constant(item).

Categories