Combine two properties to form an object with c# expressions - c#

I have a class
class MyClass
{
string Foo { get; set; }
int Bar { get; set; }
//... Other properties
}
Now what I have is two expressions
Expression<Func<MyClass, string>> expr1 = x => x.Foo;
Expression<Func<MyClass, bool>> expr2 = x => x.Bar > 0;
Using this two expressions, I want to create an expression which is something like this
Expression<Func<MyClass, object>> expr = x => new { Foo = x.Foo, Baz = x.Bar > 0 };
In other words I want to combine two properties into one anonymous object using expression trees.
My problem is,
How do I create a similar expression from the given two expressions?
I want to use final expression in a join statement with entity framework query.
The bool part is complex and need to generate dynamically.
If any alternative idea to this, that would be helpful too.

Related

Filtering but property and child entity property

I got a small problem with building dynamic expression tree for search logic. Creating an expression tree for entity own properties is working fine, but I've no idea how to add an expression which will filter by child entity properties.
Here is my Task entity:
public class Task: Entity
{
public TaskType Type { get; set; }
public TaskPriority Priority { get; set; }
public int ProjectId { get; set; }
public Project Project { get; set; }
}
And here is Project entity:
public class Project: Entity
{
public int CustomerId { get; set; }
public Customer Customer { get; set; }
}
And logic for building dynamic expression:
public Func<TaskItem, bool> Build(IList<Filter> filters)
{
ParameterExpression param = Expression.Parameter(typeof(TaskItem), "task");
List<Filter> priorityFilter = FilterFilters(filters, "Priority");
List<Filter> typeFilter = FilterFilters(filters, "Type");
List<Filter> customerFilter = FilterFilters(filters, "CustomerId");
Expression expression = null;
// BuildExpression is a method which simply creates expression which is using Tasks properties (like Type or Priority)
expression = BuildExpression(param, priorityFilter, expression);
expression = BuildExpression(param, typeFilter, expression);
// This part need's to be reworked
ParameterExpression projectParam = Expression.Parameter(typeof(Project), "project");
Expression projectCustomerExpression = Expression.Equal(Expression.PropertyOrField(projectParam, "CustomerId"), Expression.Constant(customerFilter[0].Value));
Expression customerExpression = Expression.Equal(Expression.PropertyOrField(param, "Project"), projectCustomerExpression);
Expression finall = expression != null ? Expression.AndAlso(expression, projectCustomerExpression) : projectCustomerExpression;
// End of filtering by CutomerId
return Expression.Lambda<Func<TaskItem, bool>>(finall, param).Compile();
}
I've no idea how to filter by CustomerId. The part above code marked as This part need's to be reworked is probably wrong or at least the last part of it. The idea is to extend existing expression (this one build by BuildExpression method) with an expression which will filter by CustomerId.
I already lost some time on this, trying on my own and looking for answers but with no results.
Any help?
As you have provided a minimal code, how you are creating the actual expressions is unknown. So, I will try to provide a general recipe for this scenario.
If you want to filter a list of Task then you still need to use the same ParameterExpression of type Task, like you have already done:
ParameterExpression param = Expression.Parameter(typeof(TaskItem), "task");
There is no need to create another ParameterExpression of type Project even if you want to filter on properties of Project. In stead you just need to reuse the former ParameterExpression. Note that if we build a static predicate like below, this is also the case that we also don't use a different parameter expression:
queryableTask.Where(t => t.Priority == TaskPriority.High && t.Project.CustomerId == 123);
Now to dynamically build filter on navigational (child) property, the key is to form the left expression (i.e. expression for navigational property) correctly.
Lets say our navigational property is in dot notation: Project.CustomerId. Then we can do something like this to create the left expression for property:
// We already have the following commented properties
// prop = "Project.CustomerId";
// ParameterExpression param = Expression.Parameter(typeof(TaskItem), "task");
var leftExpr = prop.Split('.')
.Aggregate<string, MemberExpression>(null,
(acc, p) => acc == null
? Expression.Property(param, p)
: Expression.Property(acc, p));
And then you can do the rest like a normal property, such as creating the right expression and combining them with another expression defining the operator (Equal, Not Equal etc.).
Hope this helps.

How to rebuild a lambda expression to start one level deeper?

I created a generic method that accepts a member access expression identifying a grouping key, just as one would pass to IQueryable<T>.GroupBy.
private static IQueryable<ObjectWithRank<T>> IncludeBestRankPerGroup<T,TGroupKey>(this IQueryable<T> q, Expression<Func<T, TGroupKey>> keySelector)
class ObjectWithRank<T> {
public T RankedObject { get; set; }
public int Rank { get; set; }
}
The IncludeBestRankPerGroup method is a variation of my IncludeRank method that just takes an IQueryable<T> and applies a rank to each element by wrapping it in ObjectWithRank<T>, returning an IQueryable<ObjectWithRank<T>>. I then want to group by the keySelector and select the best ranked element per group.
This requires me to convert a lambda expression from form 1 to 2 so I can pass it to IQueryable<ObjectWithRank<T>>.GroupBy:
(T x) => x.GroupingProperty
(ObjectWithRank<T> x) => x.RankedObject.GroupingProperty
Note that I cannot just change the root object type of the keySelector from T to ObjectWithRank<T>, because the ObjectWithRank<T> class is not exposed in the public method that calls IncludeBestRankPerGroup. The user of the API just provides an IQueryable<T>, and receives back an IQueryable<T> with the highest ranking items per group, so they never see that ObjectWithRank<T> is used under the hood.
I managed to perform the conversion with the following code, but it only works for simple member access expressions. For example, it can convert an expression like x => x.GroupingKey to x => x.RankedObject.GroupingKey, but it won't work with a two-level deep member access expression where I'd have to convert something like x => x.SubObject.GroupingKey to x => x.RankedObject.SubObject.GroupingKey.
private static Expression<Func<ObjectWithRank<T>, TGroupKey>> RebuildMemberAccessForRankedObject<T, TGroupBy>(Expression<Func<T, TGroupKey>> keySelector)
{
Expression<Func<ObjectWithRank<T>, T>> objectAccessExpression = x => x.RankedObject;
return Expression.Lambda<Func<ObjectWithRank<T>, TGroupKey>>(
Expression.Property(objectAccessExpression.Body, (keySelector.Body as MemberExpression).Member as PropertyInfo)
, objectAccessExpression.Parameters
);
}
The above seems like a hack where I first create a member access expression that access the T RankedObject property of the ObjectWithRank<T>, then tack on the provided keySelector member access expression. I'm not sure if there's a simple way to get this to work. It seems like Expression.Property only allows drilling down one property at a time, so maybe I need some kind of loop to rebuild the expression from the top, drilling down one property at a time.
There's a similar question here that does have a simple solution, but goes one level deeper on the opposite end of the expression, which isn't what I'm trying to do.
Alter Lambda Expression to go one level deeper
I was able to replace the root of an expression with a recursive lamba that drills down in the member expression until it reaches the parameter expression, replaces the parameter expression with the new root expression at that deepest level, then unwinds the call stack replacing each member expression's Expression with the updated inner expression all the way back to the top, then create's a new lambda with the updated expression and parameter expression set for the new root.
private static Expression<Func<TInNew, TOut>> UpdateExpressionRoot<TOut, TInOld, TInNew>(Expression<Func<TInNew, TInOld>> newRoot, Expression<Func<TInOld, TOut>> memberAccess)
{
Func<MemberExpression, MemberExpression> updateDeepestExpression = null;
updateDeepestExpression = e =>
{
if (e.Expression is MemberExpression)
{
var updatedChild = updateDeepestExpression((MemberExpression)e.Expression);
return e.Update(updatedChild);
}
if (e.Expression is ParameterExpression)
return e.Update(newRoot.Body);
throw new ArgumentException("Member access expression must be composed of nested member access expressions only.", nameof(memberAccess));
};
return Expression.Lambda<Func<TInNew, TOut>>(updateDeepestExpression(memberAccess.Body as MemberExpression), newRoot.Parameters);
}
It can be called like this:
class Car
{
Manufacturer Manufacturer { get; set; }
}
class Manufacturer
{
string ID { get; set; }
}
Expression<Func<Car, string>> groupKeySelector = x => x.Manufacturer.ID;
Expression<Func<ObjectWithRank<Car>, Car>> rankedObjectSelector = x => x.RankedObject;
var rankedGroupKeySelector = UpdateExpressionRoot(rankedObjectSelector, groupKeySelector);
//rankedGroupKeySelector.ToString() == "x.RankedObject.Manufacturer.ID"
//Essentially this replaced ParameterExpression {x} in x.Manufacturer.ID with MemberExpression {x.RankedObject}.

Combining AndAlso The parameter 'foo' was not bound in the specified LINQ to Entities query expression

I have an entity.
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
}
I want to create my own expression predicate. For that I have created a method that accepts property name and the value.
private static Expression<Func<Foo, bool>> Condition(string pName, object value)
{
var pe = Expression.Parameter(typeof(Foo), "foo");
var left = Expression.Property(pe, pName);
var right = Expression.Constant(value);
var equal = Expression.Equal(left, right);
var predicate = Expression.Lambda<Func<Foo, bool>>(equal, pe);
return predicate;
}
This is the predicate which works fine for a single condition.
using (var db = new MyEntities())
{
var predicate = Condition("Name", "foo");
var foos = db.Foos.Where(predicate).ToArray();
}
But when I tried to combine two conditions by following this post, it throws exception.
The parameter 'foo' was not bound in the specified LINQ to Entities
query expression.
using (var db = new MyEntities())
{
var cond1 = Condition("Name", "foo");
var cond2 = Condition("Code", "bar");
var body = Expression.AndAlso(cond1.Body, cond2.Body);
var predicate = Expression.Lambda<Func<Foo,bool>>(body, cond1.Parameters[0]);
var foos = db.Foos.Where(predicate).ToArray(); // exception
}
Please enlighten me.
The problem is that ParameterExpression in LINQ expressions is identified by reference equality, but the two Parameter objects are different references. (The name in ParameterExpression only exists for debugging purposes).
(If you reread the mentioned post, it says that the method that you tried would only work if both lambdas are defined on the same ParameterExpression object).
You have two big possibilities at this stage: either you define a way for the Condition function to accept a ParameterExpression object, or you create an ExpressionVisitor that will replace the original ParameterExpression with another. (Of course, given that you want to do an AndAlso, you could also conceivably chain two Where clauses, but that is less general.)

Trying to use parent property as parameter in child collection expression; LinqKit throws "Unable to cast MethodCallExpressionN to LambdaExpression"

I'm trying to dynamically construct an expression similar to the one below, where I can use the same comparison function, but where the values being compared can be passed in, since the value is passed from a property 'higher-up' in the query.
var people = People
.Where(p => p.Cars
.Any(c => c.Colour == p.FavouriteColour));
I believe I've constructed the query correctly, but the ExpressionExpander.VisitMethodCall(..) method throws the following exception when I try to use it:
"Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpressionN' to type 'System.Linq.Expressions.LambdaExpression'"
In real-world code, using Entity Framework and actual IQueryable<T>, I often get:
"Unable to cast object of type 'System.Linq.Expressions.MethodCallExpressionN' to type 'System.Linq.Expressions.LambdaExpression'" as well.
I've constructed a LinqPad-friendly example of my problem, as simple as I could make it.
void Main()
{
var tuples = new List<Tuple<String, int>>() {
new Tuple<String, int>("Hello", 4),
new Tuple<String, int>("World", 2),
new Tuple<String, int>("Cheese", 20)
};
var queryableTuples = tuples.AsQueryable();
// For this example, I want to check which of these strings are longer than their accompanying number.
// The expression I want to build needs to use one of the values of the item (the int) in order to construct the expression.
// Basically just want to construct this:
// .Where (x => x.Item1.Length > x.Item2)
var expressionToCheckTuple = BuildExpressionToCheckTuple();
var result = queryableTuples
.AsExpandable()
.Where (t => expressionToCheckTuple.Invoke(t))
.ToList();
}
public Expression<Func<string, bool>> BuildExpressionToCheckStringLength(int minLength) {
return str => str.Length > minLength;
}
public Expression<Func<Tuple<string, int>, bool>> BuildExpressionToCheckTuple() {
// I'm passed something (eg. Tuple) that contains:
// * a value that I need to construct the expression (eg. the 'min length')
// * the value that I will need to invoke the expression (eg. the string)
return tuple => BuildExpressionToCheckStringLength(tuple.Item2 /* the length */).Invoke(tuple.Item1 /* string */);
}
If I'm doing something obviously wrong, I'd really appreciate a nudge in the right direction! Thanks.
Edit: I know that the following would work:
Expression<Func<Tuple<string, int>, bool>> expr = x => x.Item1.Length > x.Item2;
var result = queryableTuples
.AsExpandable()
.Where (t => expr.Invoke(t))
.ToList();
However, I'm trying to separate the comparison from the location of the parameters, since the comparison could be complex and I would like to re-use it for many different queries (each with different locations for the two parameters). It is also intended that one of the parameters (in the example, the 'min length') would actually be calculated via another expression.
Edit: Sorry, I've just realised that some answers will work when attempted against my example code since my example is merely masquerading as an IQueryable<T> but is still a List<T> underneath. The reason I'm using LinqKit in the first place is because an actual IQueryable<T> from an EntityFramework DbContext will invoke Linq-to-SQL and so must be able to be parsed by Linq-to-SQL itself. LinqKit enables this by expanding everything to expressions.
Solution! Thanks to Jean's answer below, I think I've realised where I'm going wrong.
If a value has come from somewhere in the query (i.e. not a value that is known before-hand.) then you must build the reference/expression/variable to it into the expression.
In my original example, I was trying to pass the 'minLength' value taken from within the expression and pass it to a method. That method call could not be done before-hand, since it used a value from the expression, and it could not be done within the expression, since you can't build an expression within an expression.
So, how to get around this? I chose to write my expressions so that they can be invoked with the additional parameters. Though this has the downside that the parameters are no longer 'named' and I could end up with an Expression<Func<int, int, int, int, bool>> or something down the line.
// New signature.
public Expression<Func<string, int, bool>> BuildExpressionToCheckStringLength() {
// Now takes two parameters.
return (str, minLength) => str.Length > minLength;
}
public Expression<Func<Tuple<string, int>, bool>> BuildExpressionToCheckTuple() {
// Construct the expression before-hand.
var expression = BuildExpressionToCheckStringLength();
// Invoke the expression using both values.
return tuple => expression.Invoke(tuple.Item1 /* string */, tuple.Item2 /* the length */);
}
OK, so what you are trying to do (the transformation from a function that takes a single argument, that returns another function that takes a single argument f(x)(y) into a function that takes two arguments f(x, y)) is known as uncurrying. Look it up! :)
Now, the issue that you have in your code is that, in the expression returned by BuildExpressionToCheckTuple, there is a method call to BuildExpressionToCheckStringLength, which is not resolved. And you cannot resolve it because it takes an argument that is embedded in the tuple parameter.
The solution is, instead of using a method call, to use a lambda expression that will be equivalent to that method call.
That is:
public Expression<Func<int, Func<string, bool>>> ExpressionToCheckStringLengthBuilder() {
return minLength =>
str => str.Length > minLength;
}
public Expression<Func<Tuple<string, int>, bool>> BuildExpressionToCheckTuple() {
// I'm passed something (eg. Tuple) that contains:
// * a value that I need to construct the expression (eg. the 'min length')
// * the value that I will need to invoke the expression (eg. the string)
// Putting builder into a variable so that the resulting expression will be
// visible to tools that analyze the expression.
var builder = ExpressionToCheckStringLengthBuilder();
return tuple => builder.Invoke(tuple.Item2 /* the length */).Invoke(tuple.Item1 /* string */);
}
So you are looking for something like this:
public static class Program
{
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public static IQueryable<T> WherePropertyEquals<T, TProperty>(
this IQueryable<T> src, Expression<Func<T, TProperty>> property, TProperty value)
{
var result = src.Where(e => property.Invoke(e).Equals(value));
return result;
}
public static IQueryable<T> WhereGreater<T, TProperty>(
this IQueryable<T> src, Expression<Func<T, TProperty>> property, TProperty value)
where TProperty : IComparable<TProperty>
{
var result = src.Where(e => property.Invoke(e).CompareTo(value) > 0);
return result;
}
public static IQueryable<T> WhereGreater<T, TProperty>(
this IQueryable<T> src, Expression<Func<T, TProperty>> left, Expression<Func<T, TProperty>> right)
where TProperty : IComparable<TProperty>
{
var result = src.Where(e => left.Invoke(e).CompareTo(right.Invoke(e)) > 0);
return result;
}
public static void Main()
{
var persons = new List<Person>()
{
new Person
{
FirstName = "Jhon",
LastName = "Smith"
},
new Person
{
FirstName = "Chuck",
LastName = "Norris"
},
new Person
{
FirstName = "Ben",
LastName = "Jenkinson"
},
new Person
{
FirstName = "Barack",
LastName = "Obama"
}
}
.AsQueryable()
.AsExpandable();
var chuck = persons.WherePropertyEquals(p => p.FirstName, "Chuck").First();
var ben = persons.WhereGreater(p => p.LastName.Length, 6).First();
var barack = persons.WhereGreater(p => p.FirstName.Length, p => p.LastName.Length).First();
}

Lambda Expression to be used in Select() query

I am trying to build a lambda expression, containing two assignments (as shown further down), that I can then pass to a Queryable.Select() method.
I am trying to pass a string variable into a method and then use that variable to build up the lambda expression so that I can use it in a LINQ Select query.
My reasoning behind it is that I have a SQL Server datasource with many column names, I am creating a charting application that will allow the user to select, say by typing in the column name, the actual column of data they want to view in the y-axis of my chart, with the x-axis always being the DateTime. Therefore, they can essentially choose what data they chart against the DateTime value (it’s a data warehouse type app).
I have, for example, a class to store the retrieved data in, and hence use as the chart source of:
public class AnalysisChartSource
{
public DateTime Invoicedate { get; set; }
public Decimal yValue { get; set; }
}
I have (purely experimentaly) built an expression tree for the Where clause using the String value and that works fine:
public void GetData(String yAxis)
{
using (DataClasses1DataContext db = new DataClasses1DataContext())
{
var data = this.FunctionOne().AsQueryable<AnalysisChartSource>();
//just to get some temp data in....
ParameterExpression pe = Expression.Parameter(typeof(AnalysisChartSource), "p");
Expression left = Expression.MakeMemberAccess(pe,
typeof(AnalysisChartSource).GetProperty(yAxis));
Expression right = Expression.Constant((Decimal)16);
Expression e2 = Expression.LessThan(left, right);
Expression expNew = Expression.New(typeof(AnalysisChartSource));
LambdaExpression le = Expression.Lambda(left, pe);
MethodCallExpression whereCall = Expression.Call(
typeof(Queryable), "Where", new Type[] { data.ElementType },
data.Expression,
Expression.Lambda<Func<AnalysisChartSource, bool>>(e2, new ParameterExpression[] { pe }));
}
}
However……I have tried a similar approach for the Select statement, but just can’t get it to work as I need the Select() to populate both X and Y values of the AnalysisChartSource class, like this:
.Select(c => new AnalysisChartSource
{ Invoicedate = c.Invoicedate, yValue = c.yValue}).AsEnumerable();
How on earth can I build such an expression tree….or….possibly more to the point…..is there an easier way that I have missed entirely?
I find that the best way to work out how to build expression trees is to see what the C# compiler does. So here's a complete program:
using System;
using System.Linq.Expressions;
public class Foo
{
public int X { get; set; }
public int Y { get; set; }
}
class Test
{
static void Main()
{
Expression<Func<int, Foo>> builder =
z => new Foo { X = z, Y = z };
}
}
Compile that, open the results in Reflector and set the optimisation to .NET 2.0. You end up with this generated code for the Main method:
ParameterExpression expression2;
Expression<Func<int, Foo>> expression =
Expression.Lambda<Func<int, Foo>>(
Expression.MemberInit(
Expression.New((ConstructorInfo) methodof(Foo..ctor), new Expression[0]),
new MemberBinding[] { Expression.Bind((MethodInfo) methodof(Foo.set_X),
expression2 = Expression.Parameter(typeof(int), "z")),
Expression.Bind((MethodInfo) methodof(Foo.set_Y),
expression2) }
),
new ParameterExpression[] { expression2 });
Basically, I think Expression.MemberInit is what you're after.

Categories

Resources