How to use Expression.MakeIndex in Linq Expressions? - c#

Property indexer array
trying to dynamically generate the following lambda expression:
Expression<Func<Program, string>> y = _ => _.x[0];
Where x is of type List
Attempting to use Expression.MakeIndex, but it seems to be bouncing an exception:
Expression.MakeIndex(parameter, typeof (Program).GetProperty("x"), new[] {Expression.Constant(0)})
The exception message:
Incorrect number of arguments supplied for call to method
'System.Collections.Generic.List`1[System.String] get_x()'
How can I make this right?

There are two operations going on here:
Get x from parameter
Access item at index 0
You need to create two separate expressions for this:
var property = Expression.Property(parameter, typeof (Program).GetProperty("x"));
var itemAtPosition0 = Expression.MakeIndex(property, typeof(List<string>).GetProperty("Item"),
new [] { Expression.Constant(0) });
"Item" refers to the default name of the indexer property. For more information about this name and how to reliably detect the actual name used, have a look at this answer.

This answer assumes that the Program class is defined like this:
public class ProgramZ
{
public List<string> x { get; set; }
}
The problem is that you are trying to applying the indexing to the Program.x property, when it really should be applied to the List<string>'s indexer property (which is called Item).
In the end, to be able to Invoke the expression you need to wrap it into a lambda.
Here's the code to do that:
var expr =
Expression.Lambda<Func<Program, string>>(
Expression.MakeIndex(
Expression.Property(
parameter,
typeof(Program).GetProperty("x")),
typeof(List<string>).GetProperty("Item"),
new[] { Expression.Constant(0) }),
parameter);
And here's how to invoke the expression:
var instance = new ProgramZ { x = new List<string> { "a", "b" } };
Console.WriteLine(expr.Compile().Invoke(instance));
This code will output a, as expected.

Related

How to concatenate a property expression and a lambda using Select?

I would like to create the following expression dynamically:
e.Collection.Select(inner => inner.Property)
I created this code to do it, however I have an issue when I execute the expression call, someone knows what I'm doing wrong?
private static Expression InnerSelect<TInnerModel>(IQueryable source, ParameterExpression externalParameter, string complexProperty)
{
// Creates the expression to the external property. // this generates: "e.Collection".
var externalPropertyExpression = Expression.Property(externalParameter, complexProperty);
// Creates the expression to the internal property. // this generates: "inner => inner.Property"
var innerParameter = Expression.Parameter(typeof(TInnerModel), "inner");
var innerPropertyExpression = Expression.Property(innerParameter, "Property");
var innerLambda = Expression.Lambda(innerPropertyExpression, innerParameter);
return Expression.Call(typeof(Queryable), "Select", new [] { typeof(TInnerModel) }, externalPropertyExpression, innerLambda);
}
Error:
No generic method 'Select' 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.
So, first off, the primary problem is very simple. As the error message says, you haven't passed enough type arguments to Select. But when you fix that, you'll still have a problem, and that problem will be much harder for you to see and understand.
Let's dig into that.
You wish to represent this as an expression tree:
e.Collection.Select(inner => inner.Property)
Let's begin by rewriting it in its non-extension-method form.
Queryable.Select<A, B>(e.Collection, inner => inner.Property)
Where A is the collection member type and B is the type of Property.
Now, suppose you had this expression in your program. What would it actually do at runtime? It would construct an expression tree for the lambda and pass it to Queryable.Select. That is, it would do something like:
var innerParameter = parameterFactory(whatever);
var lambdaBody = bodyFactory(whatever);
var lambda = makeALambda(lambdaBody, innerParameter);
Queryable.Select<TInnerModel>(e.Collection, lambda);
Right? You with me so far?
Now, suppose we wish to translate this program fragment to an expression tree that could itself be the body of a lambda. That would be:
var theMethodInfoForSelect = whatever;
var receiverE = valueFactory(whatever);
var thePropertyInfoForCollection = whatever;
var theFirstArgument = propertyFactory(receiverE, thePropertyInfoForCollection);
...
Again, with me so far? Now, the crucial question: what is the second argument? It is NOT the value of lambda, which is what you are passing. Remember, what we are doing here is constructing an expression tree which represents the code that the compiler is generating for that thing, and the expression tree that is in lambda is not that. You're mixing levels!
Let me put it this way: the compiler is expecting "add one and three". You are passing 4. Those are very different things! One of them is a description of how to obtain a number and the other one is a number. You are passing an expression tree. What the compiler is expecting is a description of how to obtain an expression tree.
So: do you have to now write code that generates expression trees for all of lambda's construction code? Thank goodness no. We provided you a handy way to turn an expression tree into a description of how to produce an expression tree, which is the Quote operation. You need to use it.
So, what is the right sequence of events that you need to do to build your expression tree? Let's walk through it:
First, you'll need a ParameterExpression of the type of e, which you already have in hand. Let's suppose that is:
ParameterExpression eParam = Expression.Parameter(typeof(E), "e");
Next, you will need a method info for the Select method. Let's suppose you can correctly get that.
MethodInfo selectMethod = whatever;
That method takes two arguments, so let's make an array of argument expressions:
Expression[] arguments = new Expression[2];
You'll need a property info for your Collection property. I assume you can get that:
MethodInfo collectionGetter = whatever;
Now we can build the property expression:
arguments[0] = Expression.Property(eParam, collectionGetter);
Super. Next we need to start building that lambda. We need a parameter info for inner:
ParameterExpression innerParam = Expression.Parameter(typeof(Whatever), "inner");
We'll need a property info for Property, which I assume you can get:
MethodInfo propertyGetter = whatever;
Now we can build the body of the lambda:
MemberExpression body = Expression.Property(innerParam, propertyGetter);
The lambda takes an array of parameters:
ParameterExpression[] innerParams = { innerParam };
Build the lambda from the body and the parameters:
var lambda = Expression.Lambda<Func<X, int>>(body, innerParams);
Now the step you missed. The second argument is the quoted lambda, not the lambda:
arguments[1] = Expression.Quote(lambda);
Now we can build the call to Select:
MethodCallExpression callSelect = Expression.Call(null, selectMethod, arguments);
And we're done.
Give someone an expression tree and you give them an expression tree for a day; teach them how to find expression trees themselves and they can do it for a lifetime. How did I do that so fast?
Since I wrote the expression tree code generator, I had some immediate familiarity with the problem that you were likely to have. But that was ten years ago, and I did not do the above entirely from memory. What I did was I wrote this program:
using System;
using System.Linq.Expressions;
public interface IQ<T> {}
public class E
{
public IQ<X> C { get; set; }
}
public class X
{
public int P { get; set; }
}
public class Program
{
public static IQ<R> S<T, R>(IQ<T> q, Expression<Func<T, R>> f) { return null; }
public static void Main()
{
Expression<Func<E, IQ<int>>> f = e => S<X, int>(e.C, c => c.P);
}
}
Now I wished to know what code was generated by the compiler for the body of the outer lambda, so I went to https://sharplab.io/, pasted in the code, and then clicked on Results --> Decompile C#, which will compile the code to IL and then decompile it back to human-readable C#.
This is the best way I know of to quickly understand what the C# compiler is doing when it builds an expression tree, regardless of whether you know the compiler source code backwards and forwards. It's a very handy tool.

Can I use a List<T> and a List<Expression> to populate a wpf datagrid?

Can I use a List<T> and a List<Expression> to populate a wpf datagrid?
{
var processes = Process.GetProcesses().ToList();
PopulateDataGrid( processes, x => x.ProcessName, x => GetSafeFilename( x ) );
}
private string GetSafeFilename( Process p )
{
try
{
return p.MainModule.FileName;
}
catch ( Exception )
{
return "";
}
}
The idea is that I want to be able to pass a list and params list of expressions to populate a datagrid. I only want to show the list of expressions on the datagrid.
I'd also like to be able to get the underlying object for the selected row.
I know I can use anonymous types like:
var list = processes.Select( x => new {TagObject = x, ProcessName = x.ProcessName, Filename = GetSafeFilename( x )} ).ToList();
But then I have to make sure not to add "TagObject" to the datagrid.
Any ideas? I realy like the idea of the syntax:
PopulateDataGrid( processes, x => x.ProcessName, x => GetSafeFilename( x ) );
But I'm not sure how to make it happen.
You want to provide a set of expressions and use each expression to create its own colum in the grid. There is a major problem you'll have to resolve: DataGrid columns are resolved using data bindings to properties of the object:
<DataGridTextColumn Header="ProcessName" Binding="{Binding ProcessName}" />
so we can map a property-accessing expression to a DataGrid column with the appropriate binding. But your second column doesn't represent a property access, but rather a method call; and you can't set the binding of a datagridcolumn to a method call:
<!-- won't work -->
<DataGridTextColumn Header="GetSafeFilenamee" Binding="{Binding GetSafeFilename}" />
In this case, because the purpose of the method is to handle the possible exception on trying to access details on MainModule; we might be able to avoid the exception with a proprty access and using WPF's target fallback mechanism. But a general mechanism that would reach into the IL of any arbitrary method to figure out the relevant property access is almost certainly out of scope for what you want to do.
Instead of PopulateDataGrid taking multiple expressions, each with its own property access, I would suggest a single expression that contains multiple property accesses. I can think of two such expressions:
an expression that returns some array
PopulateDataGrid(processes, x => new [] { x.ProcessName, x.MainModule.FileName });
or an expression that returns an anonymous type. This has the added benefit of allowing you to pass headers to the columns:
PopulateDataGrid(processes, x => new { x.ProcessName, Path = x.MainModule.FileName });
Also, I would suggest exposing it as an extension method on DataGrid. The signature could look like this:
public static void PopulateDataGrid<TElement, TFieldsExpression>(this DataGrid dg, IEnumerable<TElement> itemsSource, Expression<Func<TElement, TFieldsExpression>> fieldsExpr) {
}
We need the TFieldsExpression generic parameter so the compiler can recognize the second parameter as an expression.
The first step is to parse the multi-expression into individual headers and property accesses. You could use something like the following:
private static List<(string name, Expression expr)> ParseFields<TElement, TFieldsExpression>(Expression<Func<TElement, TFieldsExpression>> fieldsExpression) {
var body = fieldsExpression.Body;
switch (body) {
// an array initialization with elements
// (as opposed to an array initialization with bounds -- new int[5])
case NewArrayExpression newArrayExpr when body.NodeType == ExpressionType.NewArrayInit:
return newArrayExpr.Expressions.Select(x => ("", x)).ToList();
// anonymous type
// the IsAnonymous extension method is included at the end of the post
case NewExpression newExpr when newExpr.Type.IsAnonymous():
return newExpr.Constructor.GetParameters().Select(x => x.Name).Zip(newExpr.Arguments).ToList();
default:
throw new ArgumentException("Unhandled expression type.");
}
}
Then you could write the following method:
public static void PopulateDataGrid<TElement, TFieldsExpression>(this DataGrid dg, IEnumerable<TElement> itemsSource, Expression<Func<TElement, TFieldsExpression>> fieldsExpr) {
dg.ItemsSource = itemsSource;
dg.Columns.Clear();
var fields = ParseFields(fieldsExpr);
foreach (var (name, expr) in fields) {
if (expr is MemberAccessExpression mexpr) {
dg.Columns.Add(new DataGridTextColumn {
Header = name,
Binding = new Binding(mexpr.Member.Name)
})
} else {
throw new ArgumentException("Unhandled expression type.");
}
}
}
You could then call this method as follows:
dg.PopulateDataGrid(list, x => new [] {x.ProcessName, x.HasExited, x.MachineName};
Note: most of this comes from sample code which accompanies an MSDN article I've written about expression trees. The sample code handles additional expression types, long path chains (e.g. x.MainModule.FileName) and method calls to String.Format.
Helper extensions:
// using System.Reflection;
public static bool IsAnonymous(this Type type) =>
type.HasAttribute<CompilerGeneratedAttribute>() && type.Name.Contains("Anonymous") && type.Name.ContainsAny("<>", "VB$");
public static bool HasAttribute<TAttribute>(this MemberInfo mi, bool inherit = false) where TAttribute : Attribute =>
mi.GetCustomAttributes(typeof(TAttribute), inherit).Any();
public static bool ContainsAny(this string s, params string[] testStrings) =>
testStrings.Any(x => s.Contains(x));

Dynamic lambda expression (OrderBy) and nullable property type

I'm trying to dynamically create expression that will sort data from database through Entity Framework. But I encountered one problem and cannot overcome it. Maybe let me explain what I'm trying to do. My goal is to create expression like this:
x => x.Property
Where "Property" is the name of property which I'd like to specify dynamically.
Now, let's go to the class, which represents the table in database (I simplified it to make things more clear):
public class MyModelClass
{
public long MyLongProperty { get; set; }
public decimal? MyNullableDecimalProperty { get; set; } // IMPORTANT: it's nullable
}
It is my code, where I'm trying to create expression described earlier:
// Db is EntityFramework context
IQueryable<MyModelClass> list = Db.MyModels.Select(x => x);
// x =>
var argument = Expression.Parameter(list.ElementType, "x");
// x.MyNullableDecimalProperty
var propertyToOrder = Expression.Property(argument, "MyNullableDecimalProperty");
// x => x.MyNullableDecimalProperty
var finalExpression = Expression.Call(
typeof (Queryable),
"OrderBy",
new[] { list.ElementType, typeof(IComparable) },
list.Expression,
Expression.Lambda<Func<MyModelClass, IComparable>>(propertyToOrder, argument));
list = list.Provider.CreateQuery<MyModelClass>(finalExpression);
Problem occurs at 4th statement (var finalExpression = Expression.Call(...)). I get an exception:
Expression of type „System.Nullable`1[System.Decimal]” cannot be used for return type „System.IComparable”.
As far I understand the problem is with me using "IComparable" type where "MyNullableDecimalProperty" is Nullable and Nullable doesn't user IComparable interface. The exception isn't thrown when I'm ordering by "MyLongProperty" or when I replace "IComparable".
So my questions:
What type should I use to make it work with any nullable properties?
Is it possible to use one type and it will work with all properties whether they are nullable or non-nullable.
Notice: I know I can use for ex. Dynamic Linq library, but I'm not interested in this solution - I'd like to learn how to overcome it without using 3rd party libraries.
There's no reason to use IComparable. Indeed, many types that are comparable do not implement IComparable. Simply use the runtime type of whatever you're passing:
var finalExpression = Expression.Call(
typeof (Queryable),
"OrderBy",
new[] { list.ElementType, propertyToOrder.Type },
list.Expression,
Expression.Lambda(propertyToOrder, new [] { argument }));
You don't need to specify the IComparable part, and you can also use the Queryable.OrderBy / OrderByDescending methods to help you here:
IQueryable<TSource> source = .....
var sourceType = typeof(TSource);
var parameter = Expression.Parameter(sourceType, "item");
var propertyInfo = GetProperty(sourceType, propertyName);
var orderByProperty = Expression.Property(parameter, propertyInfo);
orderBy = Expression.Lambda(orderByProperty, new[] { parameter });
return Queryable.OrderBy(source, (dynamic)orderBy)
Give that a go and see how you get on, I'm pretty sure this will work for native types and nullable types.

Error when trying to cast anonymous object, Razor

I am trying to cast an array of anonymous objects, where each object looks like this:
new {type="internal",title="Linktitle",target="_blank",link="http://www.google.se"}
I have declared a Class "Link", to which the anonymous objects should be casted
class Link{
public string type {get;set;}
public string target {get;set;}
public string title {get;set;}
public string link {get;set;}
}
Now i am trying to cast the objects, like this
List<Link> links = Model.relatedLinks.Select(l => new Link{type=l.type,target=l.target,title=l.title,link=l.link}).ToList();
Then i get the error
Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type
I found this page on how to cast anonymous objects, but im doing it the same way. Or have i missed something?
If relatedLinks itself is a dynamic value, you've got two problems:
The lambda expression part as already reported
Extension methods can't be called on dynamic values (as extension methods). This affects both the Select and ToList methods.
You can work round the first by casting the lambda expression. You can work round the second by calling Enumerable.Select directly:
// Note: don't use var here. We need the implicit conversion from
// dynamic
IEnumerable<Link> query = Enumerable.Select(Model.relatedLinks,
(Func<dynamic, Link>) (l => new Link {
type = l.type,
target = l.target,
title = l.title,
link = l.link } );
var links = query.ToList();
Or for the sake of formatting:
Func<dynamic, Link> projection = l => new Link {
type = l.type,
target = l.target,
title = l.title,
link = l.link };
IEnumerable<Link> query = Enumerable.Select(Model.relatedLinks, projection);
var links = query.ToList();
If Model.relatedLinks is already IEnumerable<dynamic> (or something similar) then you can call Select as an extension method instead - but you still need to have a strongly-typed delegate. So for example, the latter version would become:
Func<dynamic, Link> projection = l => new Link {
type = l.type,
target = l.target,
title = l.title,
link = l.link };
IEnumerable<Link> query = Model.relatedLinks.Select(projection);
var links = query.ToList();

LINQ: How to force a value based reference?

I want to provide a set of filters for a user to pick from, and each filter will correspond to an Expression<Func<X, bool>>. So, I might want to take a dynamic list of available items ('Joe', 'Steve', 'Pete', etc), and create a collection of "hard-coded" filters based on those names, and let the user select which filter(s) he wants to use. My problem is that even when I try to "hard-code" my expression based on a string value from the dynamic list, the expression is still storing the value as, what looks to me, a property hanging off of an anonymous type (and I don't know how to serialize the anon. type). Sorry if this is confusing, I'm not quite sure how to articulate this.
Here's my sample code:
public class Foo
{
public string Name { get; set; }
}
static void Main(string[] args)
{
Foo[] source = new Foo[]
{
new Foo() { Name = "Steven" } ,
new Foo() { Name = "John" } ,
new Foo() { Name = "Pete" },
};
List<Expression<Func<Foo, bool>>> filterLst = new List<Expression<Func<Foo, bool>>>();
foreach (Foo f in source)
{
Expression<Func<Foo, bool>> exp = x => x.Name == f.Name;
filterLst.Add(exp);
}
}
}
My problem is that when I look at when I look at the body of my expression, it reads as follows:
(x.Name = value(ConsoleApplication1.Program+<>c__DisplayClass3).value)
When what I really want is for the first one to read like this:
(x.Name = "Steven")
(if I change my code to this, instead, that's exactly what I get:
Expression<Func<Foo, bool>> exp = x => x.Name == "Steven";
)
I've tried forcing my value to a local string value before sticking it into the Expression, but it doesn't seem to help:
List<Expression<Func<Foo, bool>>> filterLst = new List<Expression<Func<Foo, bool>>>();
foreach (Foo f in source)
{
string value = f.Name;
Expression<Func<Foo, bool>> exp = x => x.Name == value;
filterLst.Add(exp);
}
I don't understand why (or really even how) it's still looking at some anonymous type even once I'm using a local variable that is declared to a string. Is there a way to make this work as I want it to?
The anon-type is actually the compiler-generated type it is using to perform capture of the variables. With delegates you can hack around this by implementing the capture by hand, but not with lambda expressions compiled to expression-trees.
Two choices:
build the expression tree explicitely on code via Expression.Constant etc
learn how to handle the anon types
The latter isn't too bad actually; they are just MemberExpression typically, although I have some code kicking around that covers this in full detail. I can also provide examples of buildin the expression tree, but I'm not at a PC at the moment and it doesn't lend itself well to iPod typing...
From a brief read of the question I'd look at the first option more than the second.
Oh, and watch out; the first foreach code in the question looks susceptible to the notorious l-value capture issue ;)
Edit: I found a PC ;p
var param = Expression.Parameter(typeof(Foo), "x");
var body = Expression.Equal(
Expression.PropertyOrField(param, "Name"),
Expression.Constant(f.Name, typeof(string)));
var exp = Expression.Lambda<Func<Foo, bool>>(body, param);
filterLst.Add(exp);
Marc Gravell's answer is correct, and here's how you'd implement his first choice:
var filters =
from f in source
let param = Expression.Parameter(typeof(Foo),"x")
select Expression.Lambda<Func<Foo, bool>>(
Expression.Equal(
Expression.Property(param, "Name"),
Expression.Constant(f.Name)), param);
But this typically isn't necessary. Your second example of the for loop should work with all major LINQ providers. Is there a reason you need the expression to use constants?

Categories

Resources