zzzprojects / Eval-Expression.NET

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

Support of DynamicObject or IDictionary<string, object> as context object #44

Closed tomasceleda closed 5 years ago

tomasceleda commented 5 years ago

If I run

var values = new ServiceExample2() { { "X", 1 }, { "Y", 2 } };
int result = context.Execute<int>("X + Y", values);

where ServiceExample2 is inherits from Dictionary<string, object> it works as expected. When it implements IDictionary<string, object> or inherits DynamicObject and implements the properties dynamically, it throws NullReferenceException.

Is there some other way how to run on object with has not the properties known at compile time?

Thanks

JonathanMagnan commented 5 years ago

Hello @tomasceleda ,

There is a lot of way to do it.

For example: https://dotnetfiddle.net/zclC3l

// ExpandoObject
{
    dynamic dynamicObj = new ExpandoObject();
    dynamicObj.X = 1;
    dynamicObj.Y = 2;

    Console.WriteLine(Eval.Execute<int>("X + Y", dynamicObj));
}

// VariableFactory
{
    var context = new EvalContext();
    context.VariableFactory = arg => {
        if(arg.Name == "X")
        {
            arg.Value = 1;
            arg.IsHandled = true;
        }
        else if (arg.Name == "Y")
        {
            arg.Value = 2;
            arg.IsHandled = true;
        }
    };

    Console.WriteLine(context.Execute<int>("X + Y"));
}

We will look about your both scenario to see why it throw an error.

Best Regards,

Jonathan

TehWardy commented 5 years ago

Interesting ... I hit this ... https://stackoverflow.com/questions/53104874/querying-dynamic-data#53114267

Apart from the typo in my query code (referring to properties that don't exist I found that the problem was much the same.

I'm using Eval like this ...

public static class ObjectQueryExtensions
{
    static ObjectQueryExtensions()
    {
        EvalManager.DefaultContext.IncludeMemberFromAllParameters = true;
    }

    public static TResult Query<T, TResult>(this T source, Query query)
    {
        var code = query.ToCSharp();
        var result = Eval.Execute<object>(code, source);
        return JsonConvert.DeserializeObject<TResult>(JsonConvert.SerializeObject(result));
    }
}

... by doing the json bit at the end I can "not worry about selections having to create new t { }" instances during the querying process for the simple cost of a bit of cpu time (hense the reason that's there).

Query in this case is a custom thing I built to wrap around Eval, what it is is a Json based structure I can build out in my web based front end to define queries on objects.

In this case (as seen from the stackoverflow post I have a dynamic object that I built from getting data from multiple places (some items, some company data, ect) that I collected together in to my Eval context object.

Having done that I noticed using Eval in this manner must be doing something like this in the background when we use "EvalContext.IncludeMemberFromAllParameters = true" ...

IEnumerable<ExpandoObject> Items = source.Items;

Whereas if it did ...

IEnumerable<dynamic> Items = source.Items;

Then I believe this would work. Lets also assume that for this whilst I actually have an ExpandoObject T is passed in as dynamic.

For the purpose of Eval here I think it might be "cleaner" in terms of user use case to always treat ExpandoObjects as dynamic although i have to admit I haven't really spent enough time to know for sure if that would cause unintended side effects.

JonathanMagnan commented 5 years ago

Hello @tomasceleda ,

This issue will be closed since we answered it.

We will continue the @TehWardy request on the newly thread he created

Best Regards,

Jonathan