How to rebuild a lambda expression to start one level deeper? - c#

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}.

Related

Access property of a generic type inside a function

I have a generic function GetDocuments<T> that's querying the CosmosDB API. The generic is constrained by a custom IDocument interface. At the moment, I'm passing an enum as an argument to this function that determines the type of the document -- however, my interface has the document type as a property, so it seems like I should be able to access that somehow instead of passing another arg.
Because my argument is in an Expression, I'm not sure how to access that value (I'm not sure if using the API to access expression params is the right approach). If I had an IDocument as an argument, it seems pretty straightforward to access it.
Given this code, how can I access the DocumentType without passing it to GetDocuments<T>?
Function definition:
public IEnumerable<T> GetDocuments<T>(Expression<Func<T, bool>> predicate, Enumerations.DocumentType type) where T : IDocument
{
var results = Client.CreateDocumentQuery<T>(GetDocumentCollectionUri(), GetFeedOptions())
.Where(predicate)
.Where(s => s.DocumentType == type)
.ToList();
return results;
}
Interface definition:
public interface IDocument
{
[JsonProperty(PropertyName = "id")]
string Id { get; set; }
[JsonProperty(PropertyName = "documentType")]
Enumerations.DocumentType DocumentType { get; }
}
Function call:
var messages = mailboxRepository.GetDocuments<MailboxMessageTemplate>(
s => s.UserId == user.ID,
Enumerations.DocumentType.MessageTemplate);
You can do that by pre-creating your expression and just adding it to your query.
Here is the expression that would do the trick.
internal static Expression<Func<T, bool>> TypeSpecificExpression<T>() where T : class
{
var parameter = Expression.Parameter(typeof(IDocument));
var member = Expression.Property(parameter, nameof(IDocument.Enumerations.DocumentType));
var contant = Expression.Constant(nameof(T));
var body = Expression.Equal(member, contant);
var extra = Expression.Lambda<Func<T, bool>>(body, parameter);
return extra;
}
You can then simple change your method to be:
public IEnumerable<T> GetDocuments<T>(Expression<Func<T, bool>> predicate) where T : IDocument
{
var results = Client.CreateDocumentQuery<T>(GetDocumentCollectionUri(), GetFeedOptions())
.Where(predicate && TypeSpecificExpression())
.ToList();
return results;
}
Obviously I don't have access to the Enumerations.DocumentType enum so you might need to do some tweeting on the value you are setting here: var contant = Expression.Constant(nameof(T));
On a side note, you should not be calling .ToList() like that on CreateDocumentQuery. You are synchornizing a query that can be a serious performance hit. You should be using the .AsDocumentQuery() method to get the query and then call query.ExecuteNextAsync when query.HasMoreResults.
On a second side note, it looks like you are trying to build something that the library Cosmonaut already does, including the feature you just asked a question for (you can find that method here). It's worth taking a look.
Disclaimer: I made Cosmonaut
So you are looking for a way to convert an Expression<Func<T, bool>> to a Func<T, bool>?
You can call Compile.
Compiles the lambda expression described by the expression tree into executable code and produces a delegate that represents the lambda expression.
var results = Client.CreateDocumentQuery<T>(GetDocumentCollectionUri(), GetFeedOptions())
.Where(predicate.Compile())
.Where(s => s.DocumentType == type)
.ToList();

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.

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

How do I make an anonymous method run in LINQ to Entities?

I'm trying to build a generic method that EF4.1 to look in both the Database and the Local memory for a particular row in a table that matches a particular criteria.
So far, this is what I have this.
This is the caller.
dbEntities.MyTables.LocalAndDb(delegate(MyTable s)
{ return s.Description.Contains("test"); });
This is LocalAndDb
public static object LocalAndDb<T>(this DbSet<T> myTable, Func<T, bool> function) where T : class
{
// look in local
var item = myTable.Local.Where(o => function((T)o)).FirstOrDefault()
// if not exist, look in the database
if (item == null)
{
Expression<Func<T, bool>> predicate = (u) => function(u);
item = myTable.Where(predicate).FirstOrDefault();
}
return item;
}
The problem is with this line.
item = myTable.Where(predicate).FirstOrDefault();
When it calls the database, it throws this error.
"The LINQ expression node type 'Invoke' is not supported in LINQ to Entities."
I imagine it's because I'm passing in an anonymous method and it doesn't know how to turn this into SQL. I thought converting it to an Expression object would do the trick but it's still not working for me.
What do I need to do to make a anonymous method become something that LINQ can turn into SQL?
To make this work, you need to pass the lambda expression to LocalAndDb as an expression tree (so that LINQ to Entities can analyze the code and translate it to SQL):
public static object LocalAndDb<T>(this DbSet<T> myTable,
Expression<Func<T, bool>> expr) where T : class {
// ...
if (item == null) {
item = myTable.Where(expr).FirstOrDefault();
}
return item;
}
Then, of course, the problem is that you cannot execute the expression tree when checking the in-memory data. One way to solve this is to use the Compile method of Expression<T>, but that will be a bit inefficient (depending on your scenario).
Another option is to just pass the condition as both function and expression tree:
public static object LocalAndDb<T>(this DbSet<T> myTable,
Func<T, boo> function, Expression<Func<T, bool>> expr) where T : class {
var item = myTable.Local.Where(o => function((T)o)).FirstOrDefault();
if (item == null) {
item = myTable.Where(expr).FirstOrDefault();
}
return item;
}
table.LocalAndDb(t => t.Foo > 10, t => t.Foo > 10);
This is a bit ugly, but it doesn't require inefficient compilation at runtime. If you want a slightly more sophisticated solution, then you can define your own type to keep pre-compiled functions:
class Precompiled<T1, T2> {
public Precompiled(Expression<Func<T1, T2>> expr) {
this.Expression = expr;
this.Function = expr.Compile();
}
public Expression<Func<T1,T2>> Expression { get; private set; }
public Func<T1,T2> Function { get; private set; }
}

How to Process Lambda Expressions Passed as Argument Into Method - C# .NET 3.5

My knowledge of Lambda expressions is a bit shaky, while I can write code that uses Lambda expressions (aka LINQ), I'm trying to write my own method that takes a few arguments that are of type Lambda Expression.
Background: I'm trying to write a method that returns a Tree Collection of objects of type TreeItem from literally ANY other object type. I have the following so far:
public class TreeItem
{
public string Id { get; set; }
public string Text { get; set; }
public TreeItem Parent { get; protected set; }
public IList<TreeItem> Children
{
get
{
// Implementation that returns custom TreeItemCollection type
}
}
public static IList<TreeItem> GetTreeFromObject<T>(IList<T> items,
Expression<Func<T, string>> id,
Expression<Func<T, string>> text,
Expression<Func<T, IList<T>>> childProperty) where T : class
{
foreach (T item in items)
{
// Errrm!?? What do I do now?
}
return null;
}
}
...which can be called via...
IList<TreeItem> treeItems = TreeItem.GetTreeFromObject<Category>(
categories, c => c.Id, c => c.Name, c => c.ChildCategories);
I could replace the Expressions with string values, and just use reflection, but I'm trying to avoid this as I want to make it strongly typed.
My reasons for doing this is that I have a control that accepts a List of type TreeItem, whereas I have dozens of different types that are all in a tree like structure, and don't want to write seperate conversion methods for each type (trying to adhere to the DRY principle).
Am I going about this the right way? Is there a better way of doing this perhaps?
There's no such type as "lambda expression". A lambda expression can either be converted into a compatible delegate type, or an expression tree.
Your existing method signature uses expression trees - but it's not at all clear that it really needs to. Try the delegate form (with a few parameter name changes):
public static IList<TreeItem> GetTreeFromObject<T>(IList<T> items,
Func<T, string> idSelector,
Func<T, string> textSelector,
Func<T, IList<T>> childPropertySelector) where T : class
Then you can do something like this:
foreach (T item in items)
{
string id = idSelector(item);
string text = textSelector(item);
IList<T> children = childPropertySelector(item);
// Do whatever you need here
}

Categories

Resources