How to add SelectMany to an Expression Tree with Expression.Call - c#

How do I achive the same result as:
var q = db.TableA.AsQueryable();
var q1 = Queryable.SelectMany(q, a => a.TableB, (a, t) => new { a = a, t = t });
var q2 = Queryable.SelectMany(q1, a=> a.a.TableC, (a, t) = new { a = a, t = t });
by creating an expression tree via Expression.Call:
MethodCallExpression returnCallExpression = Expression.Call(
typeof(Queryable),
"SelectMany",
new Type[] ??????,
query.Expression,
a => a.TableB,
(a, t) => new { a = a, t = t });
I'm researching the other overloads of Expression.Call to see if this can be achieved without declaring type.
My issue is that the number of SelectManys is determined at run-time so I can't just chain them. And each SelectMany changes the anonymous type of the IQueryable, so I'm having trouble getting around not knowing the type at compile-time.
Any ideas about how to apply n number of SelectMany to an IQueryable are greatly appreciated.

Do you actually have the lambda expressions "available" such as a => a.TableB or are they dynamic as well?
You could use something like this perhaps (based on this SO post):
public Type[] GetSelectManysAnonymousTypes<TSource, TCollection, TResult>(
IQueryable<TSource> queryable,
Expression<Func<TSource, IEnumerable<TCollection>>> collectionSelector,
Expression<Func<TSource, TCollection, TResult>> resultSelector)
{
return new [] {
typeof(Expression<Func<TSource, IEnumerable<TCollection>>>),
typeof(Expression<Func<TSource, TCollection, TResult>>) };
}
Or something a bit more complicated could give you back the Type[] together with the Expression[] i.e. array of expressions - ready to call Expression.Call().
I think the problem you are facing though is, forgetting anonymous types, you don't know what the collectionSelector parameter lambda looks like if you really have a dynamic, unknown number of SelectMany()'s chained together. Or I may be missing something...

Related

Can't convert ICollection<t> to IEnumerable<t>

I am building the expression tree:
{x => x.principal_info.First().agent_for_property_info}
It works as expected.
In fact, I need to convert it to IEnumerable instead of ICollection as you can see here.
Here is the method that works:
public Expression<Func<T, IEnumerable<K>>> GetGenericExpression<T, K>(bool first = false, params string[] properties)
{
var expression = GetNavigationPropertySelector<T, K>(typeof(T), first, properties);
var expRight = (Expression<Func<T, IEnumerable<K>>>)expression;
return expRight;
}
In the expression, I get the valid lambda expression:
{x => x.principal_info.First().agent_for_property_info}
When I am casting:
var expRight = (Expression<Func<T, IEnumerable<K>>>)expression;
I get an exception:
Unable to cast object of
type
'System.Linq.Expressions.Expression`1[System.Func`2[SomeModel1,
System.Collections.Generic.ICollection`1[SomeModel]]]'
to type
'System.Linq.Expressions.Expression`1[System.Func`2[SomeModel1
,System.Collections.Generic.IEnumerable`1[SomeModel]]]'.
I knew that ICollection inherits from IEnumerable assuming, that it something pretty easy to fix.
I researched a lot but didn't find the solution how to cast ICollection<T> to IEnumerable<T> or if this even possible?
In fact, the compiler can cast it implicitly as these lines are valid:
var queryData1 = new QueryData<contact_info, IEnumerable<property_info>>()
{
WhereClause = expression,
SelectClause = info => info.principal_info.First().agent_for_property_info
};
As this expression is a type of ICollection:
info => info.principal_info.First().agent_for_property_info
After a few hours of researching, trying different approaches, I came up with Idea, trying to overcome this exception. So the workaround I use was the suggestion from the comments:
x => x.principal_info.First().agent_for_property_info.Select(x4 => x4)
Here what I have added while building an expression:
var result1 = Expression.Call(
typeof(Enumerable), "Select", new Type[] { elementResultType, elementResultType },
resultProperty, resultExpr);
I actually didn't find the solution, but if someone will encounter such problem, maybe this answer could save his time.

Use lambda expression in another lambda expression

I need to combine two lambda expressions, in a way that the second expression "contains" the first.
I searched a lot but did not find any clear answer...
What i am trying to do is the following :
A first expression "expression1" is passed as a parameter to a method, and is simply used to define on which field or property the second lambda must operate.
Schematically, I am trying to do the following :
// simple field selector :
Expression<Func<T, string>> expression1 = obj => obj.field;
// trying to use the field selector in 2nd expression :
Expression<Func<T, bool>> e2 = obj => [[Result of expression1]].Equals("myValue");
In other words, I would like to get :
Expression<Func<T, bool>> e2 = obj => obj.field.Equals("myValue");
I need to do it this way because it is not always the Equals() method that will be called, but many different methods.
I tried to compile expression1 to a Func<T, string> in order to invoke this Func in expression2, but as I am using this with LinQ, I get an exception because LinQ does not support Invoke node types.
So my question is : is there a way to just combine the bodies of the two expressions, and how please ?
Thanks by advance !
Well, this should do the trick:
void Main()
{
var execute = Create<TestClass>(
first => first.MyStringField, // field selector
second => second.Equals("1234") // method selector
);
Console.WriteLine(execute(new TestClass("1234"))); // true
Console.WriteLine(execute(new TestClass("4321"))); // false
}
class TestClass
{
public string MyStringField;
public TestClass(string val){
MyStringField = val;
}
}
static Func<TSource, bool> Create<TSource>(
Expression<Func<TSource, string>> fieldSelector,
Expression<Func<string, bool>> methodSelector
)
{
// todo: classical validation of fieldSelector, if necessary.
// todo: classical validation of methodSelector, if necessary.
var compiledFieldSelector = fieldSelector.Compile();
var compiledMethodSelector = methodSelector.Compile();
return T => compiledMethodSelector(compiledFieldSelector(T));
}
Note, due the nature of lambda expressions, you need to validate the field selector and method selector, otherwise it's possible to do some very weird things.
Alternatively, the next approach creates lambda that does not "compose" things, in the sense, this is better since LinqToEntities shouldn't have problems interpreting the query.
static Func<TSource, bool> Create<TSource>(
Expression<Func<TSource, string>> memberSelector,
Expression<Func<string, bool>> methodSelector
)
{
// todo: classical validation of memberSelector, if necessary.
// todo: classical validation of methodSelector, if necessary.
var memberExpression = (MemberExpression)(memberSelector.Body);
var methodCallExpression = (MethodCallExpression)(methodSelector.Body);
// input TSource => ..
var input = Expression.Parameter(typeof(TSource));
var call = Expression.Call(
Expression.MakeMemberAccess(
input,
memberExpression.Member),
methodCallExpression.Method,
methodCallExpression.Arguments);
return Expression.Lambda<Func<TSource, bool>>(call, input)
.Compile();
}
Chris Eelmaa's 2nd answer is ok, just remove the .Compile() call to avoid having the Exception with Invoke :
static Expression<Func<TSource, bool>> Create<TSource>(
Expression<Func<TSource, string>> memberSelector,
Expression<Func<string, bool>> methodSelector
)
{
// todo: classical validation of memberSelector, if necessary.
// todo: classical validation of methodSelector, if necessary.
var memberExpression = (MemberExpression)(memberSelector.Body);
var methodCallExpression = (MethodCallExpression)(methodSelector.Body);
// input TSource => ..
var input = Expression.Parameter(typeof(TSource));
var call = Expression.Call(
Expression.MakeMemberAccess(
input,
memberExpression.Member),
methodCallExpression.Method,
methodCallExpression.Arguments);
return Expression.Lambda<Func<TSource, bool>>(call, input);
}
In my case this is then used like this :
(selector is an expression like u => u.id, and
request is a IQueryable<T>, both received as arguments)
Expression<Func<T, bool>> contains = Create<T>(selector, u => u.Contains(searchExpression));
IQueryable<T> result = request.Where(contains);

Complex LINQ sorting using Lambda Expressions

Does anyone have/know of an IQueryable.OrderBy extension that takes an Expression (retrieved, for example, by Reflection)? I believe the function would look something like this:
public static IQueryable<TEntity> OrderBy<TEntity>
(this IQueryable<TEntity> source, Expression sortExpression)
Expression would be assumed to be an Expression<Func<TEntity, T>> where TEntity is the same object being sorted on, and T is a type that needs to be determined in order to create the new IQueryable.
I've found many examples of extensions that take a string, including Dynamic Linq, like this:
public static IQueryable<TEntity> OrderBy<TEntity>(
this IQueryable<TEntity> source, string sortExpression)
If it's possible to take the string and use Reflection to look up the type from the object in question, it should also be possible to take the Expression, and get the value type which is right there IN the Expression.
Following is a detailed explanation of why I'd like to have this, which you may or may not need.
I have a rather large list of complex records to sort. Because the list is so long, I prefer to have the sorting done on the database side. To handle more complex properties, I've created Expressions that provide the sorting functionality, like so:
if (model.sortExpression == "PlannedValue")
{
Expression<Func<BDopp, decimal>> sorter = BDopp.PlannedValueSorter;
if (model.sortDirection == "DESC")
opps = opps.OrderByDescending(sorter).AsQueryable();
else
opps = opps.OrderBy(sorter).AsQueryable();
}
BDOpp.PlannedValueSorter retrieves a static expression from the object which allows sorting to be done without opps are still of type IQueryable:
public static Expression<Func<BDopp, decimal>> PlannedValueSorter
{
get
{
return z => z.BudgetSchedules
.Where(s => s.Type == 1)
.Sum(s => s.Value * s.Workshare * z.valueFactor / 100 / 100);
}
}
Sorting for simple properties is done with Extension methods that use Reflection to build an expression based on the name of the property passed as a string.
This works well, but for the complex types, I still need branching logic, and I'd rather not do that. What I'd rather do is check for a static property containing the expression, and then simply apply it. I can get the expression like this:
PropertyInfo info = typeof(BDopp).GetProperty(model.sortExpression + "Sorter",
BindingFlags.Static | BindingFlags.Public);
Expression expr = (Expression)info.GetValue(null, null);
For the PlannedValue property, this gets me the expression sorted in PlannedValueSorter, which I already know works.
Update:
Various digging around has gotten me what I think might be some progress:
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source,
Expression<Func<TEntity, dynamic>> sortExpression)
{
var unary = sortExpression.Body as UnaryExpression;
Type actualExpressionType = unary.Operand.Type;
actualExpressionType is in fact the return type of the Expression (for this particular property, it's decimal).
Unfortunately I'm mostly just working by trial and error, since I haven't yet wrapped my brain around how all this works, so my attempt to update the query like so is not working:
MethodCallExpression resultExp = Expression.Call(typeof(Queryable),
"OrderBy",
new Type[] { typeof(TEntity), actualExpressionType },
source.Expression, sortExpression);
return source.Provider.CreateQuery<TEntity>(resultExp);
It compiles okay, but the following error is thrown at runtime:
No generic method 'OrderBy' on type 'System.Linq.Queryable' is
compatible with the supplied type arguments and arguments. No type
arguments should be provided if the method is non-generic.
As I understand it you have an Expression and want to order/filter by it. It might surprise you how simple it is:
Expression<Func<TEntity, T>> sortExpr = ...; //you already have this
var ordered = myQuery.OrderBy(sortExpr);
OrderBy always uses an Expression so you can directly use it. If you only have a variable of type Expression (which points to an object of type Expression<Func<TEntity, T>>) and don't know statically what T is, you can do this:
Expression sortExpr = ...; //you already have this
var ordered = myQuery.OrderBy((dynamic)sortExpr);
dynamic will figure this out at runtime using reflection. No need to do reflection yourself. All that is needed here is overload resolution (performed by the C# runtime binder).
Okay, I've got a solution:
public static IQueryable<TEntity> OrderBy<TEntity>(
this IQueryable<TEntity> source,
Expression<Func<TEntity, dynamic>> sortExpression,
bool descending)
{
var unary = sortExpression.Body as UnaryExpression;
var operand = unary.Operand;
Type actualExpressionType = operand.Type;
MethodCallExpression resultExp =
Expression.Call(typeof(Queryable),
descending? "OrderByDescending" : "OrderBy",
new Type[] { typeof(TEntity), actualExpressionType },
source.Expression,
Expression.Lambda(operand, sortExpression.Parameters));
return source.Provider.CreateQuery<TEntity>(resultExp);
}
The bool descending is to allow for the standard OrderBy and OrderByDescending overloads. Two major breakthroughs here, at least for me:
Getting the Operand out of the expression.
Using Expression.Call and Expression.Lambda to create the new expression - this allows me to use an actual "Type" variable, whereas the Expression<Func<TEntity, T>> syntax requires you to use a type that's known at compile time.
Try this:
public static IEnumerable<T> OrderBy<T>(
this IEnumerable<T> collection,
string columnName
//, SortDirection direction
)
{
ParameterExpression param = Expression.Parameter(typeof(T), "x"); // x
Expression property = Expression.Property(param, columnName); // x.ColumnName
Func<T, object> lambda = Expression.Lambda<Func<T, object>>( // x => x.ColumnName
Expression.Convert(property, typeof(object)),
param)
.Compile();
Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> expression = (c, f) => c.OrderBy(f); // here you can use OrderByDescending basing on SortDirection
IEnumerable<T> sorted = expression(collection, lambda);
return sorted;
}
Usage:
IEnumerable<T> collection = ...
IEnumerable<T> ordered = collection.OrderBy("PropName");
See Code Project and sandbox.

Expression conversion

I am still a little green to Expressions and having difficulty figuring this problem out. This may be more of a Lambda expression issue over EF, but I am hoping someone can atleast point me in the correct direction:
I am attempting to do the following:
internal static IQueryable<TSource> WithSecurity<TSource>(thisIQueryable<TSource> source, Expression<Func<Security.Access, TSource, bool>> predicate, params MyRoles[] roles)
{
DL.MyContext ctx = Csla.Data.DbContextManager<DL.MyContext>.GetManager().DbContext;
var accessData = ctx.Access.Where(e => roles.Contains((MyRoles)e.Role_ID));
Expression x = predicate asLambdaExpression;
source =
from c in source
where accessData.Any(predicate)
select c;
return source;
}
On the where clause, there is clearly a problem as the predicate is of type Expression<Func<Security.Access, TSource, bool>>, but the Any is expecting Expression<Func<Security.Access, bool>>. Any assistance on how to convert will be greatly appreciated.
I think what you are looking for is the Compile method:
var compiledPredicate = predicate.Compile();
source =
from c in source
where accessData.Any(ad => compiledPredicate(ad, c))
select c;
Note that Compile is a somewhat expensive operation so if this methods gets invoked frequently with the same expression in a typical scenario consider caching the result from Compile().

InvalidOperationException: No method 'Where' on type 'System.Linq.Queryable' is compatible with the supplied arguments

(Code below has been updated and worked properly)
There is dynamic OrderBy sample from LinqPad. What I want to do is just simply apply 'Where' rather than 'OrderBy' for this sample. Here is my code:
IQueryable query =
from p in Purchases
//where p.Price > 100
select p;
string propToWhere = "Price";
ParameterExpression purchaseParam = Expression.Parameter (typeof (Purchase), "p");
MemberExpression member = Expression.PropertyOrField (purchaseParam, propToWhere);
Expression<Func<Purchase, bool>> lambda = p => p.Price < 100;
lambda.ToString().Dump ("lambda.ToString");
//Type[] exprArgTypes = { query.ElementType, lambda.Body.Type };
Type[] exprArgTypes = { query.ElementType };
MethodCallExpression methodCall =
Expression.Call (typeof (Queryable), "Where", exprArgTypes, query.Expression, lambda);
IQueryable q = query.Provider.CreateQuery (methodCall);
q.Dump();
q.Expression.ToString().Dump("q.Expression");
This code gets exception:
"InvalidOperationException: No method 'Where' on type 'System.Linq.Queryable' is compatible with the supplied arguments."
Any help is appraicated.
Cheers
Your lambda expression creation looks odd to me. You're adding another parameter for no obvious reason. You're also using Predicate<Purchase> instead of Func<Purchase, bool>. Try this:
LambdaExpression lambda = Expression.Lambda<Func<Purchase, bool>>(
Expression.GreaterThan(member, Expression.Constant(100)),
purchaseParam);
Use the lambda that Jon Skeet supplied. Perhaps he can also explain why ParameterExpression is so painful to use and requires using the same instance, instead of being able to be matched by name :)
Modify this line:
Type[] exprArgTypes = { query.ElementType };
exprArgTypes is type parameters to
IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate).
As you can see it only has one type parameter - TSource, which is Purchase. What you were doing, effectively, was calling Where method with two type parameters like below:
IQueryable<Purchase> Where<Purchase, bool>(this IQueryable<Purchase> source, Expression<Func<Purchase, bool>> predicate)
Once both of those fixes are in the expression runs with no problem.

Categories