zzzprojects / Eval-Expression.NET

C# Eval Expression | Evaluate, Compile, and Execute C# code and expression at runtime.
https://eval-expression.net/
Other
459 stars 86 forks source link

Compile doesn't fail if property doesn't exist on type #147

Closed geoffsmith closed 1 year ago

geoffsmith commented 1 year ago

Hi, I don't think I fully understand how Compile and Execute are supposed to work. I've tried using Compile something like this:

var code = "return order.NotExists;";  // NotExists doesn't exist on Order.
evalContext.Compile<Func<Order, object>>(code, "order");

The compile call doesn't fail and returns a valid Func. However, when I call the Func, then it fails at this point saying NotExists doesn't exist.

Is there a way (a setting perhaps) to have the Compile fail if there is an error like this?

JonathanMagnan commented 1 year ago

Hello @geoffsmith ,

Thank you for reporting. We will look at it.

You can currently disable the current behavior with the following options evalContext.DisableDynamicResolution = true; (however, it will also disable everything related to dynamic resolution and not only this scenario)

The problem is since we didn't find the NotExists property on the type Order, we tried again by considering the order as an ExpandoObject, so the property NotExists is solved when the code is executed. It doesn't make really sense in this scenario as we know that order was of type Order.

We will look at what we can do and why this code was initially added.

Best Regards,

Jon

geoffsmith commented 1 year ago

Great, thank you - let me know what you find :) I'll give that workaround a go for now, that should get me going for now 👍

JonathanMagnan commented 1 year ago

Hello @geoffsmith ,

After talking with my developer, this behavior is currently by design due to how some people use our library. Indeed, unlike the .NET compiler, we provide more flexibility by default.

When you provide a code that doesn't compile like this one:

var code = "return order.NotExists;";  // NotExists doesn't exist on Order.
var compiled = evalContext.Compile<Func<Order, object>>(code, "order");

Our compiler will try to find the property NotExists at runtime instead (like if you had passed an ExpandoObject.

But why? The reason is to allow people to be able to run the code with a class that inherits from Order, which has this property:

public class SuperOrder: Order
{
        public IOrder ChildOrder { get; set; }
    public string NotExists { get; set; }
}

var order = new SuperOrder();
var x = compiled(order);

Another case would be that an IOrder is returned, but you want to access a property in your Order class such as return order.ChildOrder.PropertyExists. This is not part of the IOrder interface but part of the Order. Again, the code will not compile directly in Visual Studio, but our library gives the flexibility to do it (perhaps too much by default).

As you understand, that is the current default behavior.

As said, you can remove this default behavior by disabling some dynamic resolution: evalContext.DisableDynamicResolution = true;

If needed, it could also be possible for us to create a new option that will only disable this part to ensure it needs to compile with the class provided.

So if you would like such a property because the DisableDynamicResolution = true option do more than only this, just let us know and we will do it

Let us also know if the cause of this issue was clear enough.

Best Regards,

Jon