Build LambdaExpression with custom parameter selection - c#

Complete definition of my extension method goes first.
static IQueryable<TResult> WithTicketNumbers<T, TResult>(
this IQueryable<T> q,
Expression<Func<T, Ticket>> ticketSelector,
Expression<Func<T, string, TResult>> resultSelector,
int digitsCount)
I have this IQueryable<T> q sequence, which is the target of the extension.
This method takes tickets from q selected by ticketSelector:
var tickets = q.Select(ticketSelector);
Next, and the main goal of the method, is taking some string-linq-supported-info from Ticket class and projecting both Ticket and string info next to the sequence:
var tickets2 = tickets.Select(W => new { Ticket = W, Info = W.Name + "123"});
Finally, I want my method to return an IQueryable which will select what user wants in resultSelector. This result selector picks both the ticket and the info parameters and produces what user wants it to produce. I am kind of stuck with the Expression class to create appropriate expression.
So far, I got my two parameters:
ParameterExpression tParameter = Expression.Parameter(typeof(T));
ParameterExpression stringParameter = Expression.Parameter(typeof(string));
Also, as I think, the final lambda:
Expression<Func<T, string, TResult>> lambda =
Expression.Lambda<Func<T, string, TResult>>
(/* ? */, tParameter, stringParameter);
However, I cannot figure out the body.
I can do Expression.Property to by reflection get the two properties Ticket and Info, but there is a type required, and I have anonymous type there, in tickets2.
Next, (as I guess) I need to use that lambda inside the tickets2 to produce the method result IQueryable<TResult>.
So how should I build that final expression?

I'm not exactly sure what you want - but this should help you get started at least (also check the other answers, suggestions - i.e. the dynamic linq)
(I'd still suggest you use a proper tool for that - http://nuget.org/packages/DynamicLINQ/)
It's a post I did earlier on (more of an exercise):
Converting List<string> to EntityFramework column/field list
What it does is to construct the expressions based on the string - for a very simple scenario of Select. You can use it like...
public static IEnumerable<object>
SelectAsEnumerable(this IQueryable entitySet, params string[] propertyPath)
{
return entitySet.SelectDynamic(propertyPath) as IEnumerable<object>;
}
var list = db.YourEntity.SelectAsEnumerable("Name", "ID", "TestProperty.ID").ToList();
You'd need some more work to get what you want - e.g. add better parsing and more features etc. (also this works for Select - not OrderBy etc.)

Solved:
/// <summary>
/// Returns a queryable sequence of TResult elements which is composed through specified property evaluation.
/// </summary>
public static IQueryable<TResult> WithInfo<TItem, TProperty, TResult>(this IQueryable<TItem> q, Expression<Func<TItem, TProperty>> propertySelector, Expression<Func<TItem, TProperty, TResult>> resultSelector)
{
ParameterExpression param = Expression.Parameter(typeof(TItem));
InvocationExpression prop = Expression.Invoke(propertySelector, param);
var lambda = Expression.Lambda<Func<TItem, TResult>>(Expression.Invoke(resultSelector, param, prop), param);
return q.Select(lambda);
}

Related

Generating an Expression<Func<TEntity, bool>> for multi-level expression

Apologies if this is covered in other answers. I searched all night and went through a hundred other solutions but couldn't quite put the puzzle pieces together. I've experimented with LinqPad, PredicateBuilder, ExpressionVisitors, etc. but I'm still scratching my head.
I am trying to implement something slightly more complicated than this but this code shows the issue:
public Expression<Func<TEntity, bool>> GeneratorEqualityTest<TProperty>(Expression<Func<TEntity, TProperty>> accessor, int expectedValue) {
// Help
var argument = Expression.Parameter(typeof(TEntity));
var accessorArgument = Expression.Property(argument, accessor.ToPropertyName());
// Help
Predicate = Expression.Lambda<Func<TEntity, bool>>(Expression.Equal(accessorArgument, Expression.Constant(expectedValue)), argument);
}
This works fine when accessor is something like this: x => x.Value
But not with x => x.Complex.Value or x => x.Complex.MoreComplex.Value
I'm trying to parse expressions from strings such as >=5 or (5...10] and generate Expressions that I can plug into the Where clause of a LINQ-to-EF query (and get translated to T-SQL). This is working fine for the single-level case but I can't figure out how to walk the expression.
In EF, x.Complex.MoreComplex.Value corresponds to SQL Join's. Bonus points if it's possible to convert the accessor into something I can pass into an Include() statement
I put a sample project up on Github: https://github.com/scottt732/ExpressionHelp
Since you are receiving the final value accessor, all you need it to apply the comparison function (i.e. build the body of the predicate):
public Expression<Func<TEntity, bool>> GeneratorEqualityTest<TProperty>(Expression<Func<TEntity, TProperty>> accessor, TProperty expectedValue)
{
var body = Expression.Equal(accessor.Body, Expression.Constant(expectedValue));
var predicate = Expression.Lambda<Func<TEntity, bool>>(body, accessor.Parameters);
return predicate;
}
Bonus method:
static class ExpressionUtils
{
public static IEnumerable<Expression<Func<TEntity, object>>> Includes<TEntity, TProperty>(this Expression<Func<TEntity, TProperty>> accessor)
{
var root = accessor.Parameters[0];
for (var node = accessor.Body as MemberExpression; node != null && node.Expression != root; node = node.Expression as MemberExpression)
yield return Expression.Lambda<Func<TEntity, object>>(node.Expression, root);
}
}

How to generate Expression<Func<T, TPaginatedKey>> expression based on a string?

I am implimenting Generic Entity Repository as described in Pro Asp.Net Web Api Http Web Services in
Asp.Net Tugberk Ugurlu et al.
The paginated function is as follows. Please focus on the third parameter 'keySelector'
public PaginatedList<T> Paginate<TPaginatedKey>(int pageIndex, int pageSize, Expression<Func<T, TPaginatedKey>> keySelector,
Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties)
{
m_EntitiesContext.Set<T>().OrderBy(keySelector);
... // Removed some code here.
}
The above method is called as follows.
m_Repository.Paginate<short>(cmd.Page, cmd.Take, x => x.Id, null);
So here the argument to the third parameter keySelector is x => x.Id. As you can expect, it is ordering by Id, and it is hard coded to Id. Now I want to generalize so that at run time I should be able to order by Id, ModifiedDate, FirstName, or what ever based on a string parameter such as "Id" or "ModifiedDate" or "FirstName". So can someone please how I can generate or create the Expression<Func<T, TPaginatedKey>> expression based on a string? I guess it should be simple enough, but I am unable to do it.
I believe what you are looking for is something called Expression Trees, which allows you to build dynamic expressions.
Taken from the example from the link, this is how they do a dynamic order by
MethodCallExpression orderByCallExpression = Expression.Call(
typeof(Queryable),
"OrderBy",
new Type[] { queryableData.ElementType, queryableData.ElementType },
whereCallExpression,
Expression.Lambda<Func<string, string>>(pe, new ParameterExpression[] { pe }));
// Create an executable query from the expression tree.
IQueryable<string> results = queryableData.Provider.CreateQuery<string>(orderByCallExpression);

How to build a simple property selector expression in ef6

How can I create a property selector for entity framework like this?
public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Func<T, string> property, string query)
{
return queryable.Where(e => property(e).ToLower().IndexOf(query) > -1).ToList();
}
I want the calling code to be able to be clean and simple like this:
var usernameResults = _db.Users.StandardSearchAlgorithm(u => u.Username, query);
I get a "The LINQ expression node type 'Invoke' is not supported in LINQ to Entities." error. I cannot work out how to get the expression built.
UPDATE:
Based on the answer by MBoros here is the code I ended up with. It works great.
The key to expression trees is to understand expression trees are all about breaking up what you normally write in code (like "e => e.Username.IndexOf(query)") into a series of objects: "e" gets its own object, "Username" its own object, "IndexOf()" its own object, the "query" constant its own object, and so on. The second key is to know that you can use a series of static methods on the Expression class to create various kinds of these objects, as shown below.
PropertyInfo pinfo = (PropertyInfo)((MemberExpression)property.Body).Member;
ParameterExpression parameter = Expression.Parameter(typeof(T), "e");
MemberExpression accessor = Expression.Property(parameter, pinfo);
ConstantExpression queryString = Expression.Constant(query, typeof(string));
ConstantExpression minusOne = Expression.Constant(-1, typeof(int));
MethodInfo indexOfInfo = typeof(string).GetMethod("IndexOf", new[] { typeof(string) }); // easiest way to do this
Expression indexOf = Expression.Call(accessor, indexOfInfo, queryString);
Expression expression = Expression.GreaterThan(indexOf, minusOne);
Expression<Func<T, bool>> predicate = Expression.Lambda<Func<T, bool>>(expression, parameter);
//return predicate.Body.ToString(); // returns "e => e.Username.IndexOf(query) > -1" which is exactly what we want.
var results = queryable.Where(predicate).ToList();
return results;
Now I have a real problem, but I will ask it in a separate question. My real query looks like this:
public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Func<T, string> property, string query)
{
return queryable.Where(e => property(e).IndexOf(query) > -1).Select(e=> new { Priority = property(e).IndexOf(query), Entity = e } ).ToList();
}
So I need to build an expression that returns an Anonymous Type!! Or even if I create a class to help, I need to write an expression that returns a new object. But I will include this in a separate question.
You cannot invoke CLR delegates so simply in sql. But you can pass in the property selector as an Expression tree., so your signature would be:
public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Expression<Func<T, string>> property, string query)
Calling would look the same. But now that you have an expression in your hand, you can have a look at this answer:
Pass expression parameter as argument to another expression
It gives you the tools to simply put an expression tree inside another one. In your case it would look like:
Expression<Func<T, bool>> predicate = e => property.AsQuote()(e).Contains(query);
predicate = predicate.ResolveQuotes();
return queryable.Where(predicate).ToList();
Once you are there, you still have the .ToLower().Contains() calls (use .Contains instead of the .IndexOf()> 1). This is actually tricky. Normally the db uses its default collation, so if it set to CI (case insensitive), then it will do the compare that way. If you don't have any constraints, and can adjust the db collation, I would go for that. In this case you can omit the .ToLower() call.
Otherwise check out this anser: https://stackoverflow.com/a/2433217/280562

Join two LINQ expressions together [duplicate]

Expression<Func<MyObject, string>> fn1 = x => x.PossibleSubPath.MyStringProperty;
Expression<Func<string, bool>> fn2 = x => x.Contains("some literal");
Is there a way to create a new lambda expression which basically uses the output of fn1 and uses it as input for fn2?
Expression<Func<MyObject, bool>> fnCombined = ...
I know that I can create the function at once, but the problem is that I'm making some generic code and therefore really need to be able to create these two functions separately, then combine them in such a way that Linq can use them on my database objects (Entity Framework).
So logically what we want to be able to do is create a new lambda in which it has a parameter of the input to the first function, and a body that calls the first function with that parameter and then passes the result as the parameter to the second function, and then returns that.
We can replicate that easily enough using Expression objects:
public static Expression<Func<T1, T3>> Combine<T1, T2, T3>(
Expression<Func<T1, T2>> first,
Expression<Func<T2, T3>> second)
{
var param = Expression.Parameter(typeof(T1), "param");
var body = Expression.Invoke(second, Expression.Invoke(first, param));
return Expression.Lambda<Func<T1, T3>>(body, param);
}
Sadly, EF and most other query providers won't really know what to do with that and won't function properly. Whenever they hit an Invoke expression they generally just throw an exception of some sort. Some can handle it though. In theory all the information they need is there, if they're written with the robustness to get at it.
What we can do however is, from a conceptual standpoint, replace every instance of the first lambda's parameter in that lambda's body with the parameter of a new lambda we're creating, and then replace all instances of the second lambda's parameter in the second lambda with the new body of the first lambda. Technically, if these expressions have side effects, and these parameters are used more than once, they wouldn't be the same, but as these are going to be parsed by an EF query provider they really shouldn't ever have side effects.
Thanks to David B for providing a link to this related question which provides a ReplaceVisitor implementation. We can use that ReplaceVisitor to go through the entire tree of an expression and replace one expression with another. The implementation of that type is:
class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
And now we can write our proper Combine method:
public static Expression<Func<T1, T3>> Combine<T1, T2, T3>(
this Expression<Func<T1, T2>> first,
Expression<Func<T2, T3>> second)
{
var param = Expression.Parameter(typeof(T1), "param");
var newFirst = new ReplaceVisitor(first.Parameters.First(), param)
.Visit(first.Body);
var newSecond = new ReplaceVisitor(second.Parameters.First(), newFirst)
.Visit(second.Body);
return Expression.Lambda<Func<T1, T3>>(newSecond, param);
}
and a simple test case, to just demonstrate what's going on:
Expression<Func<MyObject, string>> fn1 = x => x.PossibleSubPath.MyStringProperty;
Expression<Func<string, bool>> fn2 = x => x.Contains("some literal");
var composite = fn1.Combine(fn2);
Console.WriteLine(composite);
Which will print out:
param => param.PossibleSubPath.MyStringProperty.Contains("some literal")
Which is exactly what we want; a query provider will know how to parse something like that.

Combine two Linq lambda expressions

Expression<Func<MyObject, string>> fn1 = x => x.PossibleSubPath.MyStringProperty;
Expression<Func<string, bool>> fn2 = x => x.Contains("some literal");
Is there a way to create a new lambda expression which basically uses the output of fn1 and uses it as input for fn2?
Expression<Func<MyObject, bool>> fnCombined = ...
I know that I can create the function at once, but the problem is that I'm making some generic code and therefore really need to be able to create these two functions separately, then combine them in such a way that Linq can use them on my database objects (Entity Framework).
So logically what we want to be able to do is create a new lambda in which it has a parameter of the input to the first function, and a body that calls the first function with that parameter and then passes the result as the parameter to the second function, and then returns that.
We can replicate that easily enough using Expression objects:
public static Expression<Func<T1, T3>> Combine<T1, T2, T3>(
Expression<Func<T1, T2>> first,
Expression<Func<T2, T3>> second)
{
var param = Expression.Parameter(typeof(T1), "param");
var body = Expression.Invoke(second, Expression.Invoke(first, param));
return Expression.Lambda<Func<T1, T3>>(body, param);
}
Sadly, EF and most other query providers won't really know what to do with that and won't function properly. Whenever they hit an Invoke expression they generally just throw an exception of some sort. Some can handle it though. In theory all the information they need is there, if they're written with the robustness to get at it.
What we can do however is, from a conceptual standpoint, replace every instance of the first lambda's parameter in that lambda's body with the parameter of a new lambda we're creating, and then replace all instances of the second lambda's parameter in the second lambda with the new body of the first lambda. Technically, if these expressions have side effects, and these parameters are used more than once, they wouldn't be the same, but as these are going to be parsed by an EF query provider they really shouldn't ever have side effects.
Thanks to David B for providing a link to this related question which provides a ReplaceVisitor implementation. We can use that ReplaceVisitor to go through the entire tree of an expression and replace one expression with another. The implementation of that type is:
class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
And now we can write our proper Combine method:
public static Expression<Func<T1, T3>> Combine<T1, T2, T3>(
this Expression<Func<T1, T2>> first,
Expression<Func<T2, T3>> second)
{
var param = Expression.Parameter(typeof(T1), "param");
var newFirst = new ReplaceVisitor(first.Parameters.First(), param)
.Visit(first.Body);
var newSecond = new ReplaceVisitor(second.Parameters.First(), newFirst)
.Visit(second.Body);
return Expression.Lambda<Func<T1, T3>>(newSecond, param);
}
and a simple test case, to just demonstrate what's going on:
Expression<Func<MyObject, string>> fn1 = x => x.PossibleSubPath.MyStringProperty;
Expression<Func<string, bool>> fn2 = x => x.Contains("some literal");
var composite = fn1.Combine(fn2);
Console.WriteLine(composite);
Which will print out:
param => param.PossibleSubPath.MyStringProperty.Contains("some literal")
Which is exactly what we want; a query provider will know how to parse something like that.

Categories

Resources