System.Linq.Dynamic.Core icon indicating copy to clipboard operation
System.Linq.Dynamic.Core copied to clipboard

ExpressionParser failed to parse Func type

Open orcation opened this issue 3 years ago • 2 comments

I was trying to parse a expression string like this:

ParameterExpression p1 = Expression.Parameter(typeof(Customer), "p1");

ParsingConfig config = new ParsingConfig();
config.CustomTypeProvider = new FuncTypeProvider();

var e3 = DynamicExpressionParser.ParseLambda(/*config,*/ new ParameterExpression[] { p1 }, typeof(Func<int, bool>), "(p1.Method()).Invoke(1)");

var result = e3.Compile().DynamicInvoke(new Customer());

And the Customer class looks like this:

public class Customer
{
    public Func<int, bool> Method()
    {
        return x=>x>0;
    }
}

FuncTypeProvider is implemented like this:

public class FuncTypeProvider : IDynamicLinkCustomTypeProvider
{
    private HashSet<Type> _customTypes;
    public HashSet<Type> GetCustomTypes()
    {
        if (_customTypes != null)
            return _customTypes;

        _customTypes = new HashSet<Type>();
        _customTypes.Add(typeof(Func<int, bool>));

        return _customTypes;
    }

    public Dictionary<Type, List<MethodInfo>> GetExtensionMethods()
    {
        throw new NotImplementedException();
    }

    public Type ResolveType(string typeName)
    {
        throw new NotImplementedException();
    }

    public Type ResolveTypeBySimpleName(string simpleTypeName)
    {
        throw new NotImplementedException();
    }
}

Exception occurs when creating e3: System.Linq.Dynamic.Core.Exceptions.ParseException: 'Expression of type 'Func`2' expected'

Stack trace:

   at System.Linq.Dynamic.Core.Parser.ExpressionParser.Parse(Type resultType, Boolean createParameterCtor)
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(Type delegateType, ParsingConfig parsingConfig, Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values)
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(ParsingConfig parsingConfig, Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values)
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(ParsingConfig parsingConfig, ParameterExpression[] parameters, Type resultType, String expression, Object[] values)

Is it because I missed something that should be configured before parsing? Or is it because the lib cannot process a Func type return?

I'm quite a noob here, if there's anything wrong with my expression or the way I express, please let me know. Any help would be appreciated.

orcation avatar Feb 23 '22 08:02 orcation

  1. The issue is that you are supposed to pass the return type of the lambda but not the lambda itself into the "resultType" parameter.
  2. Consider deriving from DefaultDynamicLinqCustomTypeProvider rather than implementing the interface.
  [Test]
        public void Issue570()
        {
            var p1 = Expression.Parameter(typeof(Customer), "p1");

            var config = new ParsingConfig
            {
                CustomTypeProvider = new FuncTypeProvider()
            };

            var lambda = DynamicExpressionParser.ParseLambda(config, new[] { p1 }, typeof(bool), "p1.Method().Invoke(1)");
            var compiledLambda = lambda.Compile();

            //Actually, you don't need dynamic invoke since the lambda is parsed
            //and you know the actual type of your delegate
            var staticInvoke = ((Func<Customer, bool>)compiledLambda)(new Customer());

            TestContext.WriteLine($"Static invoke: {staticInvoke}");

            //But if, however, you want dynamic invoke it works as well
            var dynamicInvoke = compiledLambda.DynamicInvoke(new Customer());

            TestContext.WriteLine($"Dymamic invoke: {dynamicInvoke}");

        }

        public class Customer
        {
            public Func<int, bool> Method()
            {
                return x => x > 0;
            }
        }

        //Consider deriving form DefaultDynamicLinqCustomTypeProvider rather than implementing the interface
        public class FuncTypeProvider : DefaultDynamicLinqCustomTypeProvider
        {
            public override HashSet<Type> GetCustomTypes()
            {
                var customTypes = base.GetCustomTypes();
                customTypes.Add(typeof(Func<int, bool>));

                return customTypes;
            }
        }

ademchenko avatar Aug 17 '22 18:08 ademchenko

Hello, @ademchenko Thank you so much for helping. I literally confused the "returnType" of p1.Method with p1.Method.Invoke.

orcation avatar Aug 27 '22 09:08 orcation