Comment convertir une Chaîne à son équivalent Expression LINQ Arbre?

C'est une version simplifiée du problème original.

J'ai une classe de la Personne appelée:

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 un exemple:

var bob = new Person {
  Name = "Bob",
  Age = 30,
  Weight = 213,
  FavouriteDay = '1/1/2000'
}

Je tiens à écrire ce qui suit comme un chaîne dans mon éditeur de texte préféré....

(Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3

Je voudrais profiter de cette chaîne et mon instance d'objet et d'évaluer une valeur VRAI ou FAUX - c'est à dire l'évaluation d'un Func<Personne, bool> sur l'instance de l'objet.

Ici sont au courant de mes pensées:

  1. De mettre en œuvre une grammaire de base en ANTLR de soutien de base de Comparaison et d'Opérateurs Logiques. Je pense à de la copie de Visual Basic priorité et certains de la featureset ici: http://msdn.microsoft.com/en-us/library/fw84t893(SV.80).aspx
  2. Ont ANTLR créer l'AST à partir d'une chaîne de caractères.
  3. À pied de l'AST et de l'utilisation de la Prédicat Builder cadre pour créer dynamiquement la touche Func<Personne, bool>
  4. Évaluer le prédicat par rapport à une instance de la Personne, comme l'exige

Ma question est ai-je totalement overbaked cela? des alternatives?


EDIT: Solution Choisie

J'ai décidé d'utiliser la Dynamique de Linq Bibliothèque, plus précisément la Dynamique de la classe de Requête prévu dans le 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();
    }
  }
}

Résultat est de type System.Boolean, et dans ce cas, est VRAI.

Un grand merci à Marc Gravel.

Inclure Système.Linq.Dynamique package nuget, la documentation ici

  • Merci pour l'affichage de la solution complète de code avec votre question. Très apprécié.
  • que faire si vous avez une collection ou d'un peuple et à filtrer certains éléments? Personne.Âge > 3 ET par Personne.Poids > 50 ?
  • Merci. Je ne peux pas trouver DynamicExpression.ParseLambda(). D'espace de noms et d'assemblage est-il?
  • Tout bon.. Il y a une ambiguïté entre les espaces de noms. Nécessaire - à l'aide de E = Système.Linq.Expressions, en utilisant le Système.Linq.Dynamique;
  • Pourquoi faut-il utiliser " ET "au lieu de" &&". N'est-il pas censé être le code C#?
  • Il pourrait être plus performant à lancer le délégué à la touche Func<Personne,bool> et l'utilisation Invoquer au lieu de DynamicInvoke. Voir ici: stackoverflow.com/questions/12858340/...
  • Afin d'obtenir que cela fonctionne, vous devez télécharger C# Requête Dynamique de la Bibliothèque (inclus dans le \LinqSamples\DynamicQuery répertoire), accédez à CSharpSamples\LinqSamples\DynamicQuery\DynamicQuery, et ensuite, soit 1) copier le dynamic.cs classe à partir de l'exemple de projet dans votre projet ou 2) mettre dynamic.cs dans son propre projet de dll séparée avec le nom de fichier et l'espace de noms par défaut de System.Linq.Dynamic (qui est ce que j'ai fait parce que c'est plus SÈCHE.
  • Requête dynamique de la Bibliothèque est disponible en package NuGet appelé "le Système de.Linq.Dynamique"

InformationsquelleAutor Codebrain | 2009-05-04