Question

I tried to use expandoobjects in LINQ queries to have the ability to query against properties that are created during runtime, for example the headers from a csv file. It all worked fine if typing the LINQ query direct in the code as in the example:

// initialize testdata
List<ExpandoObject> hans = new List<ExpandoObject>();
string[] names = {"Apfel", "Birne", "Banane", "Orange"};
int[] ids = { 1, 2, 3, 4 };
for (int i = 0; i < 4; i++)
{
   dynamic horst = new ExpandoObject();
   ((IDictionary<string, object>)horst).Add("Fruit", names[i]);
   ((IDictionary<string, object>)horst).Add("ID", ids[i]);
   hans.Add(horst);
}

// try some LINQ queries, both are working as intended
var klaus = from dynamic x in hans where x.ID < 3 select x;
//var klaus = hans.Where(x => x.ID < 3).Select(x => x);

Then i tried to read the query from the commandline and create a dynamic LINQ query using a slighty modified version of the evaluant linq compiler.

string expression = System.Console.ReadLine();
LinqCompiler lc = new LinqCompiler(expression);
lc.AddSource<ExpandoObject>("hans", hans);
IEnumerable<ExpandoObject> klaus = (IEnumerable<ExpandoObject>)lc.Evaluate();

As long as as i don´t use WHERE or ORDER BY statements, everything is fine, but if any WHERE or ORDER BY is included in the query, i get an error when compiling the codedom code in the linq compiler: CS1963: An expression tree may not contain a dynamic operation.

The code for the query is created using the following line:

doRequestMethod.Statements.Add(new CodeMethodReturnStatement(new CodeSnippetExpression(Query)));

I suppose that the codedom compiler is building the expression tree in some way different to the way a direct typed in LINQ query is parsed. Any idea to get this to work would be appretiated, including other ideas to dynamically create queries for runtime-generated objects.

Was it helpful?

Solution

To get the error you're getting, I had to fix the LINQ Compiler to support dynamic, by telling it to use C# 4.0 and add a reference to Microsoft.CSharp.dll, so I assume you have done the same.

The problem is a source in LINQ compiler can be any collection, including IQueryable<T>. And if IQueryable<T> is supposed to work correctly, you actually need to treat it as IQueryable<T>, not IEnumerable<T>. The way LINQ compiler solves this is that it treats any source as IQuerybale<T>by using the AsQueryable() extension method.

This means the generated code looks like this:

public object DoRequest(System.Linq.IQueryable<System.Dynamic.ExpandoObject> hans) {
    return from dynamic x in hans where x.ID < 3 select x;
}

The problem with this code is that tries to use IQuerybale<T> versions of LINQ methods, that use Expressions. And, as the error message tells you, Expressions don't support dynamic.

I think the easiest way to fix this is to modify LINQ Compiler to use IEnumerable<T>, instead of IQuerybale<T> by changing the AddSource() into:

public void AddSource<T>(string name, IEnumerable<T> source) 
{
    this.sources.Add(new SourceDescription(name, typeof(IEnumerable<T>), source));
}

Of course, this means it won't work well for database queries, but you can't make database queries work with dynamic anyway.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top