Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)");
like image 958
Magnus Avatar asked Aug 19 '13 12:08

Magnus


2 Answers

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".

like image 160
xanatos Avatar answered Oct 10 '22 07:10

xanatos


I know there is already an accepted answer on this but it did not work for me. I am using Dynamic Linq 1.1.4. I wanted to do a query like this

$.GetNewestRisk() == null

Where GetNewestRisk() is a public method on the object represented by $. I kept getting this error "Error running query, Methods on type 'Patient' are not accessible (at index 2)".

I found in the source code there is a GlobalConfig object that allows a custom provider to be assigned which will hold all of the types you may want to work with. Here is the source code for the custom provider:

public class CustomTypeProvider: IDynamicLinkCustomTypeProvider
{
    public HashSet<Type> GetCustomTypes()
    {
        HashSet<Type> types = new HashSet<Type>();
        types.Add(typeof(Patient));
        types.Add(typeof(RiskFactorResult));
        types.Add(typeof(PatientLabResult));
        types.Add(typeof(PatientVital));
        return types;
    }
}

Here is how I am using it:

System.Linq.Dynamic.GlobalConfig.CustomTypeProvider = new CustomType();

After making this call I am able to call methods on the objects inside of the expression.

like image 43
Kywillis Avatar answered Oct 10 '22 05:10

Kywillis