Search code examples
c#linqlinq-to-objectsdynamic-linq

Call function in dynamic linq


I'm trying to call a function in a dynamic linq select statement, but im getting error:

No property or field 'A' exists in type 'Tuple2'

Example code:

void Main()
{
    var a = new Tuple<int, int>(1,1);
    var b = new[]{ a };
    var q = b.AsQueryable().Select("A.Test(it.Item1)");

    q.Dump();
}

public static class A
{
    public static int Test(int i)
    {
        return i++;
    }
}

How should I change my code to get this working?

If I call built in function Convert.ToInt32 for example it works fine.

var q = b.AsQueryable().Select("Convert.ToInt32(it.Item1)");

Also how do I cast a property using dynamic linq?

var q = b.AsQueryable().Select("((float)it.Item1)");

Solution

  • I'll say that the dynamic-linq isn't "strong enough" to do these things. It looks for methods only in the given objects and some special classes: Math, Convert, the various base types (int, float, string, ...), Guid, Timespan, DateTime

    The list of these types is clearly visible if you use ilspy/reflector on the file. They are in System.Linq.Dynamic.ExpressionParser.predefinedTypes .

    Now, clearly I could be wrong, but this works: .Select("Guid.NewGuid().ToString()").Cast<string>().ToArray()

    showing that it's quite probable that that is the "correct" list.

    There is an article here on how to modify Dynamic LINQ if you are interested http://www.krizzcode.com/2012/01/extending-dynamiclinq-language.html

    Now, an intelligent man would take the source of dynamic linq and simply expand that array... But here there aren't intelligent men... There are only programmers that want blood! Blood but especially innards!

    var type = typeof(DynamicQueryable).Assembly.GetType("System.Linq.Dynamic.ExpressionParser");
    
    FieldInfo field = type.GetField("predefinedTypes", BindingFlags.Static | BindingFlags.NonPublic);
    
    Type[] predefinedTypes = (Type[])field.GetValue(null);
    
    Array.Resize(ref predefinedTypes, predefinedTypes.Length + 1);
    predefinedTypes[predefinedTypes.Length - 1] = typeof(A); // Your type
    
    field.SetValue(null, predefinedTypes);
    

    Do this (with the types you want) BEFORE the first call to Dynamic Linq (because after the first call the methods/properties of these types are cached)

    Explanation: we use reflection to add our object(s) to this "special list".