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.
Related
I have an Expression which could be any value. For example it could be () => 5 + 5. How would I use that Expression to search a List<int> to return true if the value is found in the List<int>? What I'm trying to do is I'm given an Expression and I'm checking a list to see if the value of the Expression is in the list
private int FindRecord(System.Linq.Expressions.Expression expression)
{
// expression when stepping through in debug is {() => 5 + 5}
List<int> list = new List<int>();
list.Add(5);
list.Add(10);
return list.Any(expression); // syntax issue - cannot convert from Expression to Func<int,bool>
}
EDIT : I misunderstood the question so here is my new answer:
private bool FindRecord(System.Linq.Expressions.Expression<Func<int>> expression)
{
int valueToSearch = expression.Compile()();
List<int> list = new List<int>();
list.Add(5);
list.Add(10);
return list.Any(i => i == valueToSearch); // syntax issue - cannot convert from Expression to Func<int,bool>
}
The return type of you function should be a "bool" (Any return a bool, not an int).
Parameter type "Expression" is not accurate enough, you should say it's a expression of a function return an integer.
You need to compile this expression to be able to "run" it and to get the actual value.
Well, you can do it as follows without Compile() on the Expression parameter, but it's really not a good idea. You generally don't want to be passing Expression around in your code as it will fail at runtime instead of compile time. You should change your method to accept an Expression of the expected type instead, or make it generic.
private static bool FindRecord(Expression expression, IEnumerable<int> list)
{
ParameterExpression parameter = Expression.Parameter(typeof(int), "x");
Expression body = Expression.Equal(parameter, Expression.Invoke(expression));
Expression<Func<int, bool>> lambda = (Expression<Func<int, bool>>)Expression.Lambda(body, parameter);
Console.WriteLine(lambda);
return list.Any(lambda.Compile());
}
Newbie LINQ Expressions question-
Expression<Func<TLookupModel, TValue>> idSelector;
IEnumerable<TLookupModel> source;
TValue id;
I'm trying to do (pseudo-code):
source.AsQueryable().FirstOrDefault(x => idSelector == id)
My feeble attempt thus far is along the lines of:
var expressionParam = idSelector.Parameters.First();
ConstantExpression selectedValueConstant = Expression.Constant(id, typeof(TValue));
var idEqualExpression = Expression.Equal(idSelector, selectedValueConstant);
var lambda = Expression.Lambda<Func<TLookupModel, bool>>(idEqualExpression, expressionParam);
var selectedSourceItem = source.AsQueryable().FirstOrDefault(lambda);
I think that gives you a guess as to how I've been thinking so far. I've tried with and without the parameters, different combinations of Expression method calls, trying to get the "parameter" to come from the FirstOrDefault() call, but after reading lots of tutorials I can't get my head around how to extend a "member" expression to equal a constant in this way.
You got really close.
Your idExpression is an Expression in the form of x => x.Property. However, you're passing the whole expression to the Equal expression. Change that to pass only the body:
var idEqualExpression = Expression.Equal(idSelector.Body, selectedValueConstant);
Then you can compile the lambda and pass it to FirstOrDefault without casting to a queryable:
var selectedSourceItem = source.FirstOrDefault(lambda.Compile());
I have this method in a class called Invoice:
public static Expression<Func<Invoice, bool>> IsAllocated()
{
return i => i.TotalAmountDue == i.GetAllocationsTotal();
}
I have a list like this:
IQueryable<Invoice> invoices
And I need to filter it like that (it's Linq to Entity):
var filteredInvoices = invoices.Where(i => Invoice.IsAllocated());
In this line I'm getting two errors:
Cannot resolve method ... candidates are .... one in Enumerable and the other on in Queryable.
And also:
Cannot convert expression type Expression<Func<Invoice,bool>> to
return type 'bool'
I've tried a lot of things I've found in SO with no luck. Can someone say me what is missing here or at least, which one of the two errors is at the root of the problem?
Your method returns an appropriate expression tree already - you just need to call it, not call it in a lambda expression:
var filteredInvoices = invoices.Where(Invoice.IsAllocated());
Expression are representation and not delegate by themselves. You should create a delegate out of it first
static Expression<Func<Invoice, bool>> IsAllocatedExpr()
{
return i => i.TotalAmountDue == i.GetAllocationsTotal();
}
public static Func<Invoice, bool> IsAllocated = IsAllocatedExpr().Compile();
and then
var filteredInvoices = invoices.Where(i => Invoice.IsAllocated(i));
Say I have the following.
foreach (var loop in helper.Loop(x => x.LoopItems))
{
loop.Text(x => x.Name);
loop.Span(x => x.Name);
foreach (var loopItem in loop.Loop(x => x.NestedLoopItems))
{
loopItem.Text(x => x.Age);
}
}
This works just create with my current implementation, however it has to compile the inner lambda expression as many times as there are loop items. Currently this does something like this to create the expression to access a List<T> indexer. eg. x.ListItems[i]
var methodCallExpression = Expression.Call(_expression, ((PropertyInfo) _expression.Member).PropertyType.GetMethod("get_Item"), Expression.Constant(i));
var expression = Expression.Lambda<Func<TModel, T>>(methodCallExpression, _expression.GetParameter<TModel>());
It then does
var newExpression = CombineExpression(listExpression);
var enumerable = newExpression.Compile().Invoke(_htmlHelper.ViewData.Model);
And it is the compile step that seems to be the expensive one.
Would there be any way to cache this given the fact that it needs to create a new one for each loop, such that i in Expression.Constant(i) needs to increment each time modifying the expression.
I don't know what CombineExpression does but if you can change from Func<TModel, T> to Func<TModel, int, T> (assuming the indexer is always an int) if not you could add another generic to the method since you are already passing in "i" to get the Constant in the expression.
Also not entirely sure what type _expression is either so I don't know if this exactly is calling the overloads I think it is.
var parameterExpression = Expression.Parameter(typeof(int), "i");
var methodCallExpression = Expression.Call(_expression, ((PropertyInfo) _expression.Member).PropertyType.GetMethod("get_Item"), parameterExpression);
var expression = Expression.Lambda<Func<TModel, int, T>>(methodCallExpression, _expression.GetParameter<TModel>(), parameterExpression);
Then you could compile expression and get Func<TModel, int, T> and when invoking the Func you would pass in the "i" value.
Again since I don't know what CombineExpression does but if you get a strongly typed Func out of it you can just call it without the invoke.
Another side note why do expressions to get access to a list indexer? Why not just use IEnumerable<> or worst cast if you don't know they type but need the objects cast to IEnumerable (non-generic) and iterate over that?
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.