Generating Action with Expressions as strings

c# dynamic expression-trees

Question

I'm attempting to determine how to create an Action from a group of strings that reflect the lines that make up the action's "statement"

using System.Linq.Dynamic;

Action<T> BuildAction<T>(T sourceObject, T destinationObject) where T : BaseThing
{
    var source = Expression.Parameter(sourceObject.GetType(), "source");
    var destination = Expression.Parameter(destinationObject.GetType(), "destination");

    var statements = new[] {
        "destination.Foo = source.Foo",
        "destination.X = source.Y"
    };

    var parsedStatements = statements.Select(s => DynamicExpression.Parse(new[] { destination, source }, typeof(void), s);

    return Expression.Lambda<Action<T>>(Expression.Block(parsedStatements));
}

The goal is to produce something similar to...

Action<T> result = (destination, source) => {
     destination.Foo = source.Foo;
     destination.X = source.Y;
};

Another problem I have is that source and destination don't necessarily have to have the same type; instead, they merely share a base type. In this case, source and destination may not both have an X property and a Y property (hense the mapping).

A Report

I now have a partial answer, however it just maps "destination" and makes other assumptions that I wish to disprove. Foo is the source. Bar-type items, with no current ability to delve deeper I reasoned that by letting others know where I'm heading with this, they may be able to assist me come up with a more thorough answer.

The concept is to perform activities and then as part of the internal engine it creates this Action to transfer calculated data to the next activity before execution. As I stated in the comments, this is a tiny portion of how my workflow engine works.

I own this structure.

struct PropertySourceInfo
{
    public Activity Source { get; set; }
    public Activity Destination { get; set; }
    public Link Link { get; set; }
}

Which is returned by "SourceInfoFor(activity, p)" in the code below, where my query is caused by the select block.

Action<Activity> BuildAssign(Activity activity, Flow flow)
{
    var type = activity.GetType();
    var destination = Expression.Parameter(typeof(Activity), "activity");

    // build property mappings
    var assigns = type.GetProperties()
        .Where(p => IsPreviousActivityInput(activity, p))
        .Select(p => {
            var info = SourceInfoFor(activity, p, flow);
            if (info != null)
            {
                var i = info.Value;
                var sidx = activity.Previous.IndexOf(sa => sa == i.Source);
                var sType = activity.Previous[sidx].GetType().GetCSharpTypeName();

                // ok my assumption here is that I have something like this ...
                // {destination}.Property = {Source}.Property
                // ... so breaking it up I can then build the Expression needed for each part: 
                var assignParts = i.Link.Expression.Split(' ');

                //TODO: do this more intelligently to handle "sub property value passing"
                var destExpr = Expression.Property(Expression.Convert(destination, type), assignParts[0].Split(".".ToCharArray()).Last());
                var destArray = Expression.Property(destination, type, "Previous");
                var sourceActivity = Expression.ArrayAccess(destArray, Expression.Constant(sidx));
                var sourceExpr = Expression.Property(Expression.Convert(sourceActivity, activity.Previous[sidx].GetType()), assignParts[2].Split(".".ToCharArray()).Last());

                var result = Expression.Assign(destExpr, sourceExpr);
                return result;
            }
            else
                return null;
        })
        .Where(i => i != null)
        .ToArray();

    // the complete block as a single "Action<TActivity>" that can be called
    if (assigns.Any())
    {
        var result = Expression.Lambda<Action<Activity>>(Expression.Block(assigns), destination);
        log.Debug(result.ToString());
        return result.Compile();
    }
    else
        return null;
}

Take notice

While this issue may be answered in other ways on this occasion, I need it done this manner for reasons unrelated to the subject because of the form factor that stack expects of us when presenting a question.

Expression trees interest me as well, and I want to learn more about them.

1
0
8/9/2018 1:21:53 PM

Accepted Answer

So it seems that the solution to this was not as straightforward as I had imagined. I must, therefore, create an expression parser.

I can utilize the code in my partial solution for the question's simplest situation, but in order to provide a complete answer, I'll need to create an expression parser that can handle strings with a lot more complexity.

Since my circumstance calls for "reuse of the compiled action at scale," I am unable to use reflection since utilizing dictionaries or similar methods only addresses a portion of the fundamental problem (which I had lightly touched upon in the question).

I could consult a number of inquiries and responses that address different aspects of this issue, but I was able to discover a more "full" starting point elsewhere.

https://archive.codeplex.com/?p=simproexpr

... this example is capable of parsing expression blocks in addition to simply processing expressions.

I'm planning to develop something similar using that or something similar to address my problem; perhaps this will be helpful to others.

1
8/16/2018 9:44:54 AM


Related Questions





Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow