Reputation: 33
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.
Upvotes: 3
Views: 1905
Reputation: 244878
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 Expression
s. And, as the error message tells you, Expression
s 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.
Upvotes: 2