Create New Expression With Different Lambda Signature From Existing Expression Body - c#

Is it possible to do what I'm looking for here:
namespace ExpressionProblem
{
class Program
{
private static readonly List<dynamic> Items = new List<dynamic>
{
new { Name = "Foo" },
new { Name = "Bar" }
};
static void Main()
{
var result = DoSomething<Item>(p => p.Name == "Foo");
Console.WriteLine(result.Name);
}
static T DoSomething<T>(Expression<Func<T, bool>> expression)
{
//change expression lambda from Func<T, bool> to Func<dynamic, bool> so below will compile and work
return Items.FirstOrDefault(expression);
}
}
public class Item
{
public string Name { get; set; }
}
}
Basically I need to take the given expression with has a signature of
Expression<Func<T, bool>>
and get and expression which performs the same action but has the signature
Expression<Func<dynamic, bool>>.
Is this even possible? From what I've read I don't think you can change an existing expression as most places I've researched have said you basically have to build a new expression from an existing one. I tried to do the following:
var newExpression = Expression.Lambda<Func<dynamic, bool>>(expression.Body);
...but am getting the error "Incorrect number of parameters supplied for lambda declaration" when trying to create the new expression.
Any ideas on how to get this to work or am I trying to do something that can't (or shouldn't) be done?
Thanks in advance for any help you can provide.
Regards,
Craig
[EDIT] - I know some will ask why I don't just make the list of type Item - in my particular case I am not able to as the list to be queried will be in an app domain that has no knowledge of the Item type. I'm just including it in the same namespace/class here for brevity's sake.[/EDIT]

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

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

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 can I get property name strings used in a Func of T

I have a scenario where I have to get an array of strings that represent each of the property names used within a Func parameter. Here is an example implementation:
public class CustomClass<TSource>
{
public string[] GetPropertiesUsed
{
get
{
// do magical parsing based upon parameter passed into CustomMethod
}
}
public void CustomMethod(Func<TSource, object> method)
{
// do stuff
}
}
Here would be an example usage:
var customClass = new CustomClass<Person>();
customClass.CustomMethod(src => "(" + src.AreaCode + ") " + src.Phone);
...
var propertiesUsed = customClass.GetPropertiesUsed;
// propertiesUsed should contain ["AreaCode", "Phone"]
The part I'm stuck on in the above is the "do magical parsing based upon parameter passed into CustomMethod."
You should use Expression<Func<>> class instead. The expression contains actual tree, and can easily be complied to get a delegate (which is a func). What You are really trying to do is to look at the body of the expression and reason about it. Expression class provides You with all the neccessary infrastructure.
You'll need to change your CustomMethod to take an Expression<Func<TSource, object>>, and probably subclass the ExpressionVisitor, overriding VisitMember:
public void CustomMethod(Expression<Func<TSource, object>> method)
{
PropertyFinder lister = new PropertyFinder();
properties = lister.Parse((Expression) expr);
}
// this will be what you want to return from GetPropertiesUsed
List<string> properties;
public class PropertyFinder : ExpressionVisitor
{
public List<string> Parse(Expression expression)
{
properties.Clear();
Visit(expression);
return properties;
}
List<string> properties = new List<string>();
protected override Expression VisitMember(MemberExpression m)
{
// look at m to see what the property name is and add it to properties
... code here ...
// then return the result of ExpressionVisitor.VisitMember
return base.VisitMember(m);
}
}
This should get you started in the right direction. Let me know if you need some help figuring out the "... code here ..." part.
Useful links:
Expression Trees
How to Modify Expression Trees
ExpressionVisitor
VisitMember
MemberExpression

Categories

Resources