Action génératrice avec des expressions comme des chaînes

c# dynamic expression-trees

Question

J'essaie de comprendre comment générer une action à partir d'une collection de chaînes. Les lignes représentent les actions "déclaration" ...

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));
}

L'idée est de se retrouver avec quelque chose comme ...

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

Mon autre problème est que la source et la destination ne doivent pas nécessairement être du même type, elles ne partagent qu'un type de base. Dans cet exemple, la destination peut ne pas avoir de propriété Y et la source peut ne pas avoir de propriété X (car le mapping )

Une mise à jour

Donc, j'ai une solution partielle, bien que cela fasse une tonne d'hypothèses que je veux supprimer et que cela mappe uniquement {destination} .Foo = {source} .Les types de type Bar ne peuvent pas approfondir pour le moment, j'ai pensé que cela pourrait aidez les autres à déterminer où je veux en venir et aidez-moi ainsi à trouver une solution plus complète ...

Donc, comme je l'explique dans les commentaires, il s'agit d'un petit morceau du fonctionnement de mon moteur de flux de travail. L'idée est d'exécuter des activités, puis dans le moteur interne, il génère cette action pour copier les valeurs calculées dans l'activité suivante avant exécution.

J'ai cette struct ...

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

Ce qui est retourné par "SourceInfoFor (activity, p)" dans le code ci-dessous, le bloc select étant la cause première de ma question ...

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;
}

Notez s'il vous plaît

Pour le facteur de forme que la pile exige de nous lorsque nous posons une question, j’estimais que le fait de poser tout mon problème était un problème beaucoup plus important; .

J'aime aussi et veux une compréhension plus profonde des arbres d'expression!

Réponse acceptée

Il s'est donc avéré que la réponse à cette question n'était pas aussi simple que je l'avais espéré. En bref ... je dois écrire un analyseur d'expression.

Pour le cas le plus simple (celui posé dans la question), je peux utiliser le code dans ma solution partielle, mais pour la solution complète, je vais devoir créer un analyseur syntaxique d'expression capable de gérer beaucoup plus de complexité que les chaînes. .

Dans mon cas, l’utilisation de dictionnaires ou d’approches similaires ne résout qu’une partie du problème sous-jacent, et je ne peux pas utiliser la réflexion parce que ma situation justifie une "réutilisation de l’action compilée à grande échelle" (que j’avais légèrement évoquée dans la question).

Je pourrais me référer à une liste de questions avec des réponses permettant de résoudre divers problèmes, mais j’ai réussi à trouver un point de départ plus "complet" pour ce que j’essayais de réaliser ailleurs ...

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

... cet exemple peut faire un peu plus que simplement analyser des expressions, il est également capable d'analyser des blocs d'expression.

En utilisant cela / quelque chose comme ça, je vais construire quelque chose dans ce sens pour résoudre mon problème, j'espère que cela aidera les autres.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi