How do I create a dynamic Labda Expression using an outer variable? - c#

I have a scenario where I have two tables 'side' and 'item'.
I'm trying to create the following query:
_db.SideTable.Where(s => s.Active && !_db.ItemTable.Any(i => i.SIDE_1 == s.ID));
But where SIDE_1 is actually dynamic (SIDE_1 through SIDE_20) where the number 1-20 comes from a variable 'sideId'.
I've tried creating doing the following:
// Access the parameter's SIDE_xxx property
ParameterExpression param = Expression.Parameter(typeof(ItemTable));
Expression property = Expression.Property(param, "SIDE_" + sideId);
// Access the outer variable's ID property
ParameterExpression var = Expression.Variable(typeof(SideTable), "s");
Expression property2 = Expression.Property(var, "ID");
// Make the ID property value int? since the SIDE_xxx property may be null and otherwise the expression will fail
var valExpr = Expression.Convert(property2, typeof(int?));
// The body of the expression is simply an equal check
Expression body = Expression.Equal(property, valExpr);
// Create the expression
var expression = Expression.Lambda<Func<Artikel, bool>>(body, param);
// Get the data
_db.SideTable.Where(s => s.Active && !_db.ItemTable.Any(expression)).ToList();
When I run this however I'm greeted with the following exception:
The parameter 's' was not bound in the specified LINQ to Entities query expression
I guess I'm not using the right syntax to access the outer 's' property correctly, but I have no idea how to do it otherwise.

Related

C# Expression trees - combine multiple method calls

I'm using .Net Core and expression trees.
I have a Product class, where LstProp property contains list of values separated by ';', like "val1;val2;val3;":
public class Product
{
// actually contains list of values separated by ';'
public string LstProp{get; set;}
}
And I want to filter Products by this property and contains any condition using expression trees.
I've try this, but it doesn't work.
var value="val1;val2"
var productItem = Expression.Parameter(typeof(Product), "product");
var prop = Expression.Property(productItem, "LstProp");
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var values = value.ToString().Split(';');
Expression predicate = null;
foreach (var val in values)
{
var listPropExpression = Expression.Constant(val);
var containsExpresion=Expression.Call(listPropExpression, method, property);
predicate = predicate != null ? Expression.Or(predicate, containsExpresion) : containsExpresion;
}
So I'm trying to combine call of Contains function for each value in the list, but getting error about "No conversion between BinaryExpression and MethodCallExpression".
How could I combine multiple method calls with expression trees?
I have found solution.
I got two problems:
Incorrect order of parameters.
Should be Expression.Call(property, method, listPropExpression); instead Expression.Call(listPropExpression, method, property);
Main problem was solved with simple cast:
predicate = predicate != null ? (Expression) Expression.Or(predicate, containsExpresion) : containsExpresion;
As a Result I get expression like product=>product.LstProp.Contains("val1") Or product.LstProp.Contains("val2")

C# Is there a way to resolve lambda expression`s delegate type in code

My goal is to support sorting in an application and expose it via REST API that would accept the parameter as a string.
Current implementation is along the lines of this:
GetUsers (Expression<Func<User, int>> sortExpression) {
// Retrieve users ...
return users.orderBy(sortExpression);
}
Usage example:
var users = GetUsers(u => u.Id);
the Expression<Func<User, int>> sortExpression is widely used in our repository and changing it would be difficult.
What I'd like to do is to be able to swap the u => u.Id with something that is generated during run-time.
Something that I have so far is:
// sortBy is retrieved from the calling method.
var type = typeof(User).GetProperties().FirstOrDefault(x => x.Name == sortBy).GetType();
var sortExpression = Expression.Property(Expression.Parameter(typeof(User)), sortBy);
var parameter = Expression.Parameter(typeof(User));
var expressionBody = Expression.Lambda(typeof(Func<User, int>), sortExpression, parameter);
var users = GetUsers(expressionBody)
I can see at run-time that this does create an expression that fits my needs, but the error is Argument 5: cannot convert from 'LambdaExpression' to 'Expression<System.Func<User, int>>' even though the body of the expression is supposed to be set by typeof(Func<User, int>)
I've figured out what I've been doing wrong.
First: Create the expression body using generic method
// Generic Method, return type Expression<Func<User, int>>
Expression.Lambda<Func<User, int>>(sortExpression, parameter);
Instead of passing the typeof(Func<User, int>) parameter.
// Non-generic. Return type LambdaExpression
Expression.Lambda(typeof(Func<User, int>), sortExpression, parameter);
Second:
I wasn't binding the parameter properly, which made it so that the expression was accessing property of a discarded parameter that wasn't provided to the expression.
// I'm creating an expression to access the property of a newly created parameter.
var sortExpression = Expression.Property(Expression.Parameter(typeof(User)), sortBy);
var parameter = Expression.Parameter(typeof(User));
var expressionBody = Expression.Lambda<Func<User, int>>(sortExpression, parameter);
//Above causes an unbinded variable exception since there are two parameters, one of which is not passed/bound.
//Should be:
var parameter = Expression.Parameter(typeof(User));
var sortExpression = Expression.Property(parameter, sortBy);

Complex Expression Tree with no changing context of param

i need to dinamically generate expression like this:
Expression<Func<MyClass, bool>> expr = x => (x.SomeField.CompareTo(someValue) <= 0);
trying to do it like this:
var paramExpr = Expression.Parameter(typeof(MyClass), "x");
Expression<Func<MyClass, FieldType>> pathToField = x => x.SomeField;
Expression path = pathToField;
if (!(path is LambdaExpression lambdaMember))
throw ...;
Expression valueExpr = Expression.Constant(someValue);
var bodyExpr = Expression.LessThanOrEqual(Expression.Call(lambdaMember.Body, "CompareTo", null, valueExpr ), Expression.Constant(0));
return Expression.Lambda<Func<MyClass, FieldType>>(bodyExpr, paramExpr);
but always getting error when trying to compile this:
variable 'x' of type 'MyClass' referenced from scope '', but it is not defined
how i could do this correctly?
The problem here is that you're using lambdaMember.Body, which references the x from x => x.SomeField - but because you only used the .Body, that is undefined - and is unrelated to the x from Expression.Parameter(typeof(MyClass), "x");
In the general case, there are 2 options here:
invoke the entire lambda (i.e. lambdaMember, not lambdaMember.Body) - passing in the arguments to use for the parameters
rewrite the inner lambda at runtime using ExpressionVisitor - swapping out instances of the x from the inner expression with whatever you wanted to use as the argument - presumably paramExpr
The first option is easier, and is just Expression.Invoke:
var bodyExpr = Expression.LessThanOrEqual(
Expression.Call(Expression.Invoke(lambdaMember, paramExpr),
"CompareTo", null, valueExpr), Expression.Constant(0));
Note: there is a third option in this case, since it is a relatively simple example - you can just hijack the parameter from the inner expression and use it instead of declaring paramExpr as a new parameter expression:
var paramExpr = lambdaMember.Parameters.Single();
Expression valueExpr = Expression.Constant(someValue);
var bodyExpr = Expression.LessThanOrEqual(
Expression.Call(lambdaMember.Body,
"CompareTo", null, valueExpr), Expression.Constant(0));
return Expression.Lambda<Func<MyClass, FieldType>>(bodyExpr, lambdaMember.Parameters);

Dynamic Where Linq to Entities

I've been doing research on how to dynamically make queries for linq. I'm creating a UI for a web application which allows them to select properties from a DB and set where clauses to build reports dynamically. All of the questions I've seen on stack or other sites reference using Dynamic Linq and LinqKit to Solve the problem, example Here . However I can't find an solution to express the syntax.
// This attempts to grab out a title whose from the Database whose
// Copyright Date equals 2006
propName = "CopyrightDate";
Model.Titles.Where(t => t.GetType().GetProperty(propName).GetValue(t, null) == "2006");
I want to do something like this but in Linq to Entities. Linq to entities doesn't support reflection like that, I do not want to pull out all of the data and run Linq to Object the DB is too Large. Any Suggestions on how to write this in Dynamic Linq. Bonus points if you can cast the type to the property type so It can be evaultuated with standard operators (== , > , < , etc..).
What the LINQ-to-entities Where extension method wants, is an Expression<Func<T, bool>>. You can create such an expression like this:
// Create lambda expression: t => t.CopyrightDate == "2006"
ParameterExpression parameter = Expression.Parameter(typeof(T), "t");
var lambda = Expression.Lambda<Func<T, bool>>(
Expression.Equal(
Expression.Property(parameter, "CopyrightDate"),
Expression.Constant("2006")
),
parameter
);
where T is the type of your class containing the CopyrightDate property.
var result = context.Titles
.Where(lambda)
.ToList();
A somewhat more general solution is:
Expression<Func<T, bool>> Comparison<T>(ExpressionType comparisonType,
string propertyName, object compareTo)
{
ParameterExpression parameter = Expression.Parameter(typeof(T), "t");
MemberExpression property = Expression.Property(parameter, propertyName);
ConstantExpression constant = Expression.Constant(compareTo);
Expression body = Expression.MakeBinary(comparisonType, property, constant);
return Expression.Lambda<Func<T, bool>>(body, parameter);
}
Here is how you can rewrite your query using Dynamic Linq:
var propName = "CopyrightDate";
var propValue = "2006";
Model.Titles.Where(string.Format("{0}=#0", propName), propValue);
string.Format("{0}=#0", propName) produces the query string for the Where clause, which would be "CopyrightDate=#0" in this case. #0 specifies the parameter for the query, which becomes propValue.

Maybe a Really simple, Dynamic Linq To Entities Select statement?

What I want basically is to be able to to the following (following is psuedo code)
string SelectField = cb1.Name.Substring(2);
MyContext.Items.Select(x=>x.SelectField )
I tried the following:
string SelectField = cb1.Name.Substring(2);
ParameterExpression pe = Expression.Parameter(typeof(Item), "p");
var expr = Expression.Lambda(Expression.Property(pe, SelectField), pe);
query = MyContext.Items.Select(p=>expr)
but it gave me the error:
The LINQ expression node type 'Lambda' is not supported in LINQ to Entities.
Is this possible? I just want to be able to select a single entity property based on the selection of my combobox.
I was able to get my desired results using the following code (not much different then my original attempt)
string SelectField = cb1.Name.Substring(2);
ParameterExpression pe = Expression.Parameter(typeof(Item), "p");
Expression expr = Expression.Lambda(Expression.Property(pe, SelectField), pe);
Expression<Func<Item, string>> expression = (Expression<Func<Item, string>>)expr;
var query = MyContext.Items.Select(expression);
All I was missing was the Func line. It now works as I wanted.
Ok, so with this type of solution you're likely going to have to do a bunch of reflection to get it to work dynamically.
string SelectField = cb1.Name.Substring(2);
ParameterExpression pe = Expression.Parameter(typeof(Item), "p");
First you have to invoke the lambda method without knowing the complete return type. The return type is Expression<Func<Item, ?>> (with the question mark being a placeholder for the property type.
var propertyType = typeof(Item).GetProperty(SelectField).GetGetMethod().ReturnType;
var lambdaMethodParamType = typeof(Func<,>).MakeGenericType(typeof(Item), propertyType);
var lambdaMethod = typeof(Expression).GetMethods().First(x => x.Name == "Lambda" && x.IsGenericMethod).MakeGenericMethod(lambdaMethodParamType);
var expr = lambdaMethod.Invoke(null, new object[] { Expression.Property(pe, SelectField), new ParameterExpression[] { pe } });
Then we have to invoke the select method in a similar fashion because the property type isn't know until runtime.
var selectMethod = typeof(Queryable).GetMethods().First(x => x.Name == "Select").MakeGenericMethod(typeof(Item), propertyType);
var query = (IQueryable)selectMethod.Invoke(null, new object[] { MyContext.Items, expr });
I don't know what you're doing with the query variable so I can't provide any further guidance with that at this time. Again, in this example we don't know what type will be contained in the collection until runtime because it is a collection of the property values.
FYI: My method for selecting the correct lambda and select was a total hack. You really should create a more robust search method examing the signature more completely to ensure you've found the correct method you are needing. I can provide a better example later if you need it when I have more time.

Categories

Resources