Analyse d'arbres d'expression lambda
-
04-07-2019 - |
Question
J'essaie d'utiliser des expressions Lambda dans un projet pour mapper une API de requête tierce. Je suis donc en train d’analyser l’arbre d’expression à la main.
Si je passe dans une expression lambda telle que:
p => p.Title == "title"
tout fonctionne.
Cependant, si mon expression lambda ressemble à:
p => p.Title == myaspdropdown.SelectedValue
Avec le débogueur .NET, je ne vois pas la valeur réelle de cette fonction. Au lieu de cela, je vois quelque chose comme:
p => p.Title = (value(ASP.usercontrols_myaspusercontrol_ascx).myaspdropdown.SelectedValue)
Qu'est-ce qui donne? Et lorsque j'essaie de saisir le côté droit de l'expression sous forme de chaîne, j'obtiens (valeur (ASP.usercontrols_myaspusercontrol_ascx) .myaspdropdown.SelectedValue)
au lieu de la valeur réelle. Comment puis-je obtenir la valeur réelle?
La solution
N'oubliez pas que lorsque vous utilisez l'expression lambda comme un arbre d'expression, vous n'avez pas de code exécutable. Vous avez plutôt un arbre d'éléments d'expression, qui constituent l'expression que vous avez écrite.
Charlie Calvert a une bonne publication qui discute cela en détail. Voici un exemple d'utilisation d'un visualiseur d'expressions pour le débogage d'expressions.
Dans votre cas, pour obtenir la valeur du côté droit de l'expression d'égalité, vous devez créer une nouvelle expression lambda, la compiler puis l'invoquer.
J'ai piraté ensemble un exemple rapide: espérons qu'il vous fournisse ce dont vous avez besoin.
public class Class1
{
public string Selection { get; set; }
public void Sample()
{
Selection = "Example";
Example<Book, bool>(p => p.Title == Selection);
}
public void Example<T,TResult>(Expression<Func<T,TResult>> exp)
{
BinaryExpression equality = (BinaryExpression)exp.Body;
Debug.Assert(equality.NodeType == ExpressionType.Equal);
// Note that you need to know the type of the rhs of the equality
var accessorExpression = Expression.Lambda<Func<string>>(equality.Right);
Func<string> accessor = accessorExpression.Compile();
var value = accessor();
Debug.Assert(value == Selection);
}
}
public class Book
{
public string Title { get; set; }
}
Autres conseils
Pour obtenir la valeur réelle, vous devez appliquer la logique de l'arborescence d'expression à votre contexte.
L'intérêt des arborescences d'expression est qu'elles représentent la logique en tant que données plutôt qu'en évaluant l'expression. Vous aurez besoin de comprendre ce que l'expression lambda signifie vraiment. Cela peut vouloir dire en évaluer certaines parties par rapport à des données locales - vous devrez le décider vous-même. Les arbres d'expression sont très puissants, mais l'analyse et l'utilisation ne sont pas simples. (Demandez à n'importe qui qui a écrit un fournisseur LINQ ... Frans Bouma a déploré les difficultés à plusieurs reprises.)
Je viens de me battre avec exactement le même problème, merci Bevan. Sur une extension, voici un modèle générique que vous pouvez utiliser pour extraire la valeur (en l’utilisant dans mon moteur de requête).
[TestFixture]
public class TestClass
{
[Test]
public void TEst()
{
var user = new User {Id = 123};
var idToSearch = user.Id;
var query = Creator.CreateQuery<User>()
.Where(x => x.Id == idToSearch);
}
}
public class Query<T>
{
public Query<T> Where(Expression<Func<T, object>> filter)
{
var rightValue = GenericHelper.GetVariableValue(((BinaryExpression)((UnaryExpression)filter.Body).Operand).Right.Type, ((BinaryExpression)((UnaryExpression)filter.Body).Operand).Right);
Console.WriteLine(rightValue);
return this;
}
}
internal class GenericHelper
{
internal static object GetVariableValue(Type variableType, Expression expression)
{
var targetMethodInfo = typeof(InvokeGeneric).GetMethod("GetVariableValue");
var genericTargetCall = targetMethodInfo.MakeGenericMethod(variableType);
return genericTargetCall.Invoke(new InvokeGeneric(), new[] { expression });
}
}
internal class InvokeGeneric
{
public T GetVariableValue<T>(Expression expression) where T : class
{
var accessorExpression = Expression.Lambda<Func<T>>(expression);
var accessor = accessorExpression.Compile();
return accessor();
}
}
Je ne suis pas sûr de comprendre. Où êtes-vous "voir" cette? Est-ce au moment de la conception ou de l'exécution? Les expressions lambda peuvent être considérées essentiellement comme des délégués anonymes et fonctionneront avec une exécution différée. Vous ne devez donc pas vous attendre à voir la valeur affectée avant que la ligne ait été passée, évidemment.
Je ne pense pas que ce soit vraiment ce que vous voulez dire cependant ... si vous clarifiez un peu la question, je pourrais peut-être vous aider:)