This is my first post here. If I've broken any guidelines, please let me know and I'll be happy to correct them.
I have the following entity classes:
public class Book
{
public int BookID { get; set; }
public string Author { get; set; }
public string Publisher { get; set; }
}
And a second entity class as such,
public class Library
{
public int ID { get; set; }
public Book Book { get; set; }
public int Count { get; set; }
}
I also have this function to generate a lambda expression dynamically based on user input.
public static Expression<Func<T, bool>> GetLambdaExpression<T>(List<Operation> OperationList)
{
ExpressionTree expressionTree = new ExpressionTree();
Node Root = expressionTree.ConstructTree(OperationList);
var Parameter = Expression.Parameter(typeof(T), "x");
var Expression = CalculateExpression(Root); //Returns an Expression Clause by And/Or every input in OperationList
return Expression.Lambda<Func<T, bool>>(Expression, Parameter); //Finally creating Lambda
}
Operation class contains details about the type of operation, field and values. It gets passed from the client which I'm using the query against the Entity classes by mapping field names.
code works as intended when used this way,
var OperationList = //Get from client
var LambdaExpression = GetLambdaExpression<Book>(OperationList);
var result = _BookContext.Books.Where(LambdaExpression);
OR
var OperationList = //Get from client
var LambdaExpression = GetLambdaExpression<Library>(OperationList);
var result = _LibraryContext.Library.Where(LambdaExpression);
I'm trying to Join two Entity classes using LINQ but I can't seem to find a way to dynamically create a Lambda Expression for the generated Anonymous type that is returned by the JOIN.
My join looks like,
var result = from c in _BookContext.Books
join d in _LibraryContext.Library
on c.BookID equals d.ID
select new { c , d };
However, this won't work for obvious reasons,
var OperationList = //Passed from client
var LambdaExpression = GetLambdaExpression<T>(OperationList);
result.Where(LambdaExpression);
Passing 'object' or 'dynamic' to the GetLambdaExpression() doesn't work because the field names are not pre-defined and it throws an exception.
How could I construct an Expression Tree for an Anonymous type.
Many thanks.
UPDATE
I managed to fix it. Here's what I did:
Instead of storing the result of a join into an Anonymous type, I created a new class which has objects of the Entity classes used to perform the join.
public class JoinResult
{
public Book Book { get; set; }
public Library Library { get; set; }
}
Perform the join and store the data into JoinResult
var result = from c in _BookContext.Books
join d in _LibraryContext.Library
on c.BookID equals d.ID
select new JoinResult{ Book = c , Library = d };
Finally, here's the trick to dynamically create the Lambda Expression for JoinResult.
I created an Expression Parameter for JoinResult, then created Expression Properties for the Properties of JoinResult.
I used the Expression Properties created to be used as Parameters to be passed into a new Property for the Entity class. Essentially, creating a property in the format of "x.Book.BookID".
For instance, if I wanted to perform a EqualOperation on JoinResult. Here is how I would do it.
public static IQueryable<T> PerformEqualOperation<T>(int Constant, int FieldName, Type Prop, IQueryable<T> resultOfJoin)
{
var Parameter = Expression.Parameter(typeof(T), "x"); //x
PropertyInfo[] Properties = typeof(T).GetProperties(); //Get the properties of JoinResult
string propertyname;
//Get the property name
foreach(var property in Properties)
{
if(property.GetType() == Prop)
propertyname = property.Name;
}
//Creating a property that can be passed as a parameter to the property for Entity class.
var expressionparameter = Expression.Property(Parameter, propertyname); //x.Book
var expressionproperty = Expression.Property(expressionparameter, FieldName);//x.Book.BookID
var expressionclause = Expression.Equal(expressionproperty, Expression.Constant(Constant));//x.Book.BookID == Constant
var expressionlambda = Expression.Lambda<Func<T,bool>>(expressionclause, Parameter)
return resultOfJoin.Where(expressionlambda).AsQueryable();
}
Hope this helps
What about making an extension method? Like this:
public static class QueryExpression
{
public static IQueryable<T> WhereWithLambdaExpression<T>(
this IQueryable<T> query, List<Operation> OperationList)
{
ExpressionTree expressionTree = new ExpressionTree();
Node Root = expressionTree.ConstructTree(OperationList);
var Parameter = Expression.Parameter(typeof(T), "x");
//Returns an Expression Clause by And/Or every input in OperationList
var Expression = CalculateExpression(Root);
//Finally creating Lambda
Expression<Func<T, bool>> predicate =
Expression.Lambda<Func<T, bool>>(Expression, Parameter);
return query.Where(predicate);
}
}
Then
var query = joinResults.WhereWithLambdaExpression(OperationList);
The compiler can infer the anonymous type from IQueryable<T>
and pass it as T
to the extension method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With