Il s'agit d'une version simplifiée du problème original.
J'ai une classe appelée Personne :
public class Person {
public string Name { get; set; }
public int Age { get; set; }
public int Weight { get; set; }
public DateTime FavouriteDay { get; set; }
}
...et disons une instance :
var bob = new Person {
Name = "Bob",
Age = 30,
Weight = 213,
FavouriteDay = '1/1/2000'
}
Je voudrais écrire ce qui suit comme un chaîne de caractères dans mon éditeur de texte préféré....
(Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3
Je voudrais prendre cette chaîne et mon instance d'objet et évaluer un VRAI ou un FAUX - c'est-à-dire évaluer un Func<Personne, bool> sur l'instance d'objet.
Voici ce que je pense actuellement :
- Implémenter une grammaire de base dans ANTLR pour supporter les comparaisons de base et les opérateurs logiques. Je pense copier la précédence de Visual Basic et certaines des caractéristiques ici : http://msdn.microsoft.com/en-us/library/fw84t893(VS.80).aspx
- Demandez à ANTLR de créer un AST approprié à partir d'une chaîne fournie.
- Marchez sur l'AST et utilisez le Constructeur de prédicats pour créer de manière dynamique le système Func<Person, bool>.
- Évaluer le prédicat contre une instance de Person comme requis
Ma question est de savoir si j'ai trop cuit ce plat ? Y a-t-il des alternatives ?
EDIT : Solution choisie
J'ai décidé d'utiliser la Dynamic Linq Library, plus précisément la classe Dynamic Query fournie dans les LINQSamples.
Code ci-dessous :
using System;
using System.Linq.Expressions;
using System.Linq.Dynamic;
namespace ExpressionParser
{
class Program
{
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public int Weight { get; set; }
public DateTime FavouriteDay { get; set; }
}
static void Main()
{
const string exp = @"(Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3";
var p = Expression.Parameter(typeof(Person), "Person");
var e = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { p }, null, exp);
var bob = new Person
{
Name = "Bob",
Age = 30,
Weight = 213,
FavouriteDay = new DateTime(2000,1,1)
};
var result = e.Compile().DynamicInvoke(bob);
Console.WriteLine(result);
Console.ReadKey();
}
}
}
Le résultat est de type System.Boolean, et dans ce cas, il est VRAI.
Merci à Marc Gravell.
Inclure System.Linq.Dynamic paquet nuget, documentation ici
39 votes
Merci de poster le code complet de la solution avec votre question. Nous apprécions beaucoup.
0 votes
Que faire si vous avez une collection ou des personnes et que vous souhaitez filtrer certains éléments ? Personne.Age > 3 AND Personne.Poids > 50 ?
0 votes
Merci. Je ne trouve pas DynamicExpression.ParseLambda(). Dans quel espace de noms et dans quel assemblage se trouve-t-il ?
0 votes
Tout va bien Il y avait une ambiguïté entre les espaces de noms. Nécessaire - using E = System.Linq.Expressions ; using System.Linq.Dynamic ;
1 votes
Pourquoi utilise-t-il "AND" au lieu de "&&" ? Ce n'est pas censé être du code C# ?
0 votes
Il serait peut-être plus performant de transformer le délégué en Func<Person,bool> et d'utiliser Invoke au lieu de DynamicInvoke. Voir ici : stackoverflow.com/questions/12858340/
0 votes
Pour que cela fonctionne, vous devez télécharger Bibliothèque de requêtes dynamiques C# (incluse dans l'application \LinqSamples\DynamicQuery répertoire) , naviguer vers
CSharpSamples\LinqSamples\DynamicQuery\DynamicQuery
et ensuite, soit 1) copier ledynamic.cs
de l'exemple de projet dans votre projet ou 2) mettre la classedynamic.cs
dans son propre projet comme une dll séparée avec un nom de fichier et un espace de nom par défaut deSystem.Linq.Dynamic
(c'est ce que j'ai fait parce que c'est plus DRY.0 votes
La bibliothèque de requêtes dynamiques est disponible sous la forme d'un paquet NuGet appelé "System.Linq.Dynamic".
0 votes
Des mises à jour pour .NET Core seraient très appréciées :-) :-) :-)