Search code examples
c#linqdynamiccompiler-errorsexpression-trees

Compiler Error : An expression tree may not contain a dynamic operation


Consider the following code, which wraps (rather than using inheritance for specific reasons) an instance of Dictionary<string, T> and implements IEnumerable and IQueryable so that it can be used with linq queries:

public class LinqTest<T> : IEnumerable<KeyValuePair<string, T>>, IQueryable<KeyValuePair<string, T>>
{
    private Dictionary<string, T> items = default(Dictionary<string, T>);

    public virtual T this[string key]
    {
        get { return this.items.ContainsKey(key) ? this.items[key] : default(T); }
        set { this.items[key] = value; }
    }

    public virtual T this[int index]
    {
        get { return this[index.ToString()]; }
        set { this[index.ToString()] = value; }
    }

    public Type ElementType
    {
        get { return this.items.AsQueryable().ElementType; }
    }

    public Expression Expression
    {
        get { return this.items.AsQueryable().Expression; }
    }

    public IQueryProvider Provider
    {
        get { return this.items.AsQueryable().Provider; }
    }

    public IEnumerator<KeyValuePair<string, T>> GetEnumerator()
    {
        return this.items.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.items.GetEnumerator();
    }
}

I tested this code as follows:

LinqTest<dynamic> item = new LinqTest<dynamic>();
item["a"] = 45;
item["b"] = Guid.NewGuid();
item["c"] = "Hello World";
item["d"] = true;

item.Where(o => o.Value.GetType() == typeof(Guid)).ForEach(i => Console.WriteLine(i));

//Compiler error: An expression tree may not contain a dynamic operation

This indicates to me that o.Value.GetType() == typeof(Guid) cannot be compiled into the expression because it is dynamic.

However, I tested this theory with the following code:

Dictionary<string, dynamic> item = new Dictionary<string, dynamic>();
item["a"] = 45;
item["b"] = Guid.NewGuid();
item["c"] = "Hello World";
item["d"] = true;

item.Where(o => o.Value.GetType() == typeof(Guid)).ForEach(i => Console.WriteLine(i));

This compiles, and runs without ANY errors, and contains a dynamic expression...can anyone explain, and possibly point out how I can fix my code?

Note: .ForEach is a non-standard extension method which implements a foreach loop.


Solution

  • The problem is that your type implements IQueryable<>, so the Queryable method is chosen by member lookup - so the compiler tries to create an expression tree from your lambda expression... and that's what fails.

    The dictionary example succeeds because it's using Enumerable rather than Queryable, so it's converting the lambda expression into a delegate instead.

    You can fix your code by using AsEnumerable:

    item.AsEnumerable()
        .Where(o => o.Value.GetType() == typeof(Guid))
        .ForEach(i => Console.WriteLine(i));
    

    It's not clear why you're implementing IQueryable<> at all, to be honest - another simpler option would simply be to stop doing that. Your source data is just a Dictionary, so that's already going to be using LINQ to Objects instead of anything queryable-based... why introduce IQueryable<> at all?