Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for parsing Action lambdas #425

Closed
glopesdev opened this issue Oct 5, 2020 · 8 comments · Fixed by #455
Closed

Add support for parsing Action lambdas #425

glopesdev opened this issue Oct 5, 2020 · 8 comments · Fixed by #455
Assignees
Labels

Comments

@glopesdev
Copy link
Contributor

This was possible in the old System.Linq.Dynamic package simply by allowing the specification of the delegate type passed to Expression.Lambda from one of the overloads in ParseLambda (see archived source).

This would greatly increase the flexibility of the dynamic linq library for composing arbitrary statement blocks.

@JonathanMagnan JonathanMagnan self-assigned this Oct 5, 2020
@JonathanMagnan
Copy link
Member

Hello @glopesdev ,

Thank you for reporting, we will look at it if something already similar already exists.

Best Regards,

Jon


Performance Libraries
context.BulkInsert(list, options => options.BatchSize = 1000);
Entity Framework ExtensionsEntity Framework ClassicBulk OperationsDapper Plus

Runtime Evaluation
Eval.Execute("x + y", new {x = 1, y = 2}); // return 3
C# Eval FunctionSQL Eval Function

@JonathanMagnan JonathanMagnan mentioned this issue Oct 19, 2020
@JonathanMagnan
Copy link
Member

Hello @glopesdev ,

The v1.2.4 has been finally released.

It's now possible to pass the delegate type for most method such as:

var expression = (Action<int>)DynamicExpressionParser.ParseLambda(typeof(Action<int>), new[] { Expression.Parameter(typeof(int), "x") }, typeof(int),  "x + 1").Compile();
var expression2 = (Func<int, int>)DynamicExpressionParser.ParseLambda(typeof(Func<int, int>), new[] { Expression.Parameter(typeof(int), "x") }, typeof(int), "x + 1").Compile();

var r1 = expression2(3);

Let me know if that's exactly what you were looking for.

Best Regards,

Jon

@glopesdev
Copy link
Contributor Author

@JonathanMagnan that is exactly what I was hoping for, thanks a lot for the support, now I can finally jump over from the old System.Linq.Dynamic 😉

@JonathanMagnan
Copy link
Member

Awesome @glopesdev

We are glad we sorted things out ;)

Don't hesitate to contact us for any questions, issues or feedback!

Best regards,

Jon

@niuzheng168
Copy link

@JonathanMagnan

I am trying something like this:
var expression = (Action<NestedInput>)DynamicExpressionParser.ParseLambda(typeof(Action<NestedInput>), new[] { Expression.Parameter(typeof(NestedInput), "x") }, typeof(bool), "x.NestedProp.ListProp[1].Value = \"123\"").Compile();

expression.Invoke(nestedInput);

however, the value is not changed to "123"... do you have any ideas?

@JonathanMagnan
Copy link
Member

Hello @niuzheng168 ,

Do you think you could provide a runnable project sample for this issue? It will help my developer to get started to investigate it faster and make sure nothing is missing.

Providing a project sample is now REQUIRED. It happened too many times that something was missing to investigate and/or answer an issue.

Try to create a new project with only the minimal code (having too many non-related codes doesn’t help either).

You can send it to info@zzzprojects.com if you need to keep the source private

Best Regards,

Jon


Performance Libraries
context.BulkInsert(list, options => options.BatchSize = 1000);
Entity Framework ExtensionsEntity Framework ClassicBulk OperationsDapper Plus

Runtime Evaluation
Eval.Execute("x + y", new {x = 1, y = 2}); // return 3
C# Eval FunctionSQL Eval Function

@glopesdev
Copy link
Contributor Author

glopesdev commented Oct 26, 2020

@niuzheng168 @JonathanMagnan I don't know if this is the same issue, but I'm also testing this new feature and I found a parse limitation with the following code sample:

using System;
using System.Collections.Generic;
using System.Linq.Dynamic.Core;
using System.Linq.Dynamic.Core.CustomTypeProviders;
using System.Linq.Expressions;

namespace TestExpressions
{
    class Switch
    {
        public void Press()
        {
            Console.WriteLine("!");
        }
    }

    class TypeProvider : DefaultDynamicLinqCustomTypeProvider
    {
        public override HashSet<Type> GetCustomTypes()
        {
            var customTypes = base.GetCustomTypes();
            customTypes.Add(typeof(Switch));
            return customTypes;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var s = new Switch();
            var parsing = new ParsingConfig();
            parsing.CustomTypeProvider = new TypeProvider();
            var parameter = Expression.Parameter(typeof(Switch), "it");

            // the following line will throw a System.Linq.Dynamic.Core.Exceptions.ParseException:
            // Method 'Press' in type 'Switch' does not return a value'
            var lambda = (Action<Switch>)DynamicExpressionParser.ParseLambda(typeof(Action<Switch>), parsing, new[] { parameter }, null, "it.Press()").Compile();

            lambda(s);
        }
    }
}

This seems to boil down to L1656 on ExpressionParser.cs and the following check:

if (method.ReturnType == typeof(void))
{
    throw ParseError(errorPos, Res.MethodIsVoid, id, TypeHelper.GetTypeName(method.DeclaringType));
}

The check makes sense if we expect only Func delegate types, but if the return type is typeof(void) as in the case of Action delegates, this should be allowed. Removing this check was also required to support Action delegates in the old System.Linq.Dynamic package (see zzzprojects/System.Linq.Dynamic@6ee95c6)

Removing the check is not a problem since Expression.Lambda will already do all necessary verifications and throw if the returned expression does not match the expected delegate type. Security is not compromised as the method will be checked against custom type providers as with any other function call. This makes sure that only void methods which have been explicitly allowed and configured by the caller can execute.

@niuzheng168
Copy link

niuzheng168 commented Nov 3, 2020

@JonathanMagnan try below codes:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;

namespace DynamicExpression
{
    public class ListItem
    {
        public int Id { get; set; }
        public string Value { get; set; }
    }

    public class NestedInput
    {
        public string SimpleProp { get; set; }
        public List<ListItem> NestedProp { get; set; }
    }

    class Program
    {
        static NestedInput nestedInput = new NestedInput
        {
            SimpleProp = "simpleProp",
            NestedProp = new List<ListItem>
            {
                new ListItem
                {
                    Id = 1,
                    Value = "first"
                }
            }
        };

        static void Main(string[] args)
        {
            var expression = (Action<NestedInput, int>)DynamicExpressionParser.ParseLambda(
                typeof(Action<NestedInput, int>), 
                new[] { Expression.Parameter(typeof(NestedInput), "x"), Expression.Parameter(typeof(int), "y") }, 
                null, 
                "x.NestedProp[0].Id = y").Compile();
            expression.Invoke(nestedInput, 10);
            Console.WriteLine(nestedInput.NestedProp[0].Id);
        }
    }
}

I suppose the it will nestedInput.NestedProp[0].Id will set to 10, but it still 1 after expression executed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging a pull request may close this issue.

4 participants