首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何将字符串转换为等价的LINQ表达式树?

如何将字符串转换为等价的LINQ表达式树?
EN

Stack Overflow用户
提问于 2009-05-04 18:48:00
回答 7查看 105K关注 0票数 189

这是原始问题的简化版本。

我有一个名为Person的类:

代码语言:javascript
复制
public class Person {
  public string Name { get; set; }
  public int Age { get; set; }
  public int Weight { get; set; }
  public DateTime FavouriteDay { get; set; }
}

...and假设有一个实例:

代码语言:javascript
复制
var bob = new Person {
  Name = "Bob",
  Age = 30,
  Weight = 213,
  FavouriteDay = '1/1/2000'
}

我想在我最喜欢的文本编辑器中将以下内容写成字符串...

代码语言:javascript
复制
(Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3

我想获取这个字符串和我的对象实例,并计算一个TRUE或FALSE -即计算对象实例上的Func。

以下是我目前的想法:

  1. 在ANTLR中实现了一个基本语法,以支持基本的比较和逻辑运算符。我正在考虑在这里复制Visual Basic的优先级和一些特性集:http://msdn.microsoft.com/en-us/library/fw84t893(VS.80).aspx
  2. Have ANTLR从提供的字符串中创建一个合适的AST。bool>
  3. Evaluate
  4. 遍历AST并使用Predicate Builder框架动态创建Func

我的问题是我是不是做得太过火了?有其他选择吗?

编辑:选择的解决方案

我决定使用Dynamic Linq Library,特别是LINQSamples中提供的动态查询类。

代码如下:

代码语言:javascript
复制
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();
    }
  }
}

结果的类型为System.Boolean,在本例中为TRUE。

非常感谢Marc Gravell。

包括System.Linq.Dynamic nuget包、documentation here

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2009-05-04 19:34:37

dynamic linq library在这方面会有帮助吗?具体地说,我认为这是一个Where子句。如有必要,将其放入列表/数组中,以便对其调用.Where(string)!即

代码语言:javascript
复制
var people = new List<Person> { person };
int match = people.Where(filter).Any();

如果没有,那么编写一个解析器(在幕后使用Expression )并不是很麻烦--我在圣诞节前的火车上写了一个类似的解析器(尽管我不认为我有源码)……

票数 71
EN

Stack Overflow用户

发布于 2010-02-08 19:04:26

另一个这样的库是Flee

我对Dynamic Linq LibraryFlee进行了快速比较,发现expression "(Name == \"Johan\" AND Salary > 500) OR (Name != \"Johan\" AND Salary > 300)"的速度要快10倍

这就是你用Flee编写代码的方法。

代码语言:javascript
复制
static void Main(string[] args)
{
  var context = new ExpressionContext();
  const string exp = @"(Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3";
  context.Variables.DefineVariable("Person", typeof(Person));
  var e = context.CompileDynamic(exp);

  var bob = new Person
  {
    Name = "Bob",
    Age = 30,
    Weight = 213,
    FavouriteDay = new DateTime(2000, 1, 1)
  };

  context.Variables["Person"] = bob;
  var result = e.Evaluate();
  Console.WriteLine(result);
  Console.ReadKey();
}
票数 32
EN

Stack Overflow用户

发布于 2013-11-01 19:08:15

代码语言:javascript
复制
void Main()
{
    var testdata = new List<Ownr> {
        //new Ownr{Name = "abc", Qty = 20}, // uncomment this to see it getting filtered out
        new Ownr{Name = "abc", Qty = 2},
        new Ownr{Name = "abcd", Qty = 11},
        new Ownr{Name = "xyz", Qty = 40},
        new Ownr{Name = "ok", Qty = 5},
    };

    Expression<Func<Ownr, bool>> func = Extentions.strToFunc<Ownr>("Qty", "<=", "10");
    func = Extentions.strToFunc<Ownr>("Name", "==", "abc", func);

    var result = testdata.Where(func.ExpressionToFunc()).ToList();

    result.Dump();
}

public class Ownr
{
    public string Name { get; set; }
    public int Qty { get; set; }
}

public static class Extentions
{
    public static Expression<Func<T, bool>> strToFunc<T>(string propName, string opr, string value, Expression<Func<T, bool>> expr = null)
    {
        Expression<Func<T, bool>> func = null;
        try
        {
            var type = typeof(T);
            var prop = type.GetProperty(propName);
            ParameterExpression tpe = Expression.Parameter(typeof(T));
            Expression left = Expression.Property(tpe, prop);
            Expression right = Expression.Convert(ToExprConstant(prop, value), prop.PropertyType);
            Expression<Func<T, bool>> innerExpr = Expression.Lambda<Func<T, bool>>(ApplyFilter(opr, left, right), tpe);
            if (expr != null)
                innerExpr = innerExpr.And(expr);
            func = innerExpr;
        }
        catch (Exception ex)
        {
            ex.Dump();
        }

        return func;
    }
    private static Expression ToExprConstant(PropertyInfo prop, string value)
    {
        object val = null;

        try
        {
            switch (prop.Name)
            {
                case "System.Guid":
                    val = Guid.NewGuid();
                    break;
                default:
                    {
                        val = Convert.ChangeType(value, prop.PropertyType);
                        break;
                    }
            }
        }
        catch (Exception ex)
        {
            ex.Dump();
        }

        return Expression.Constant(val);
    }
    private static BinaryExpression ApplyFilter(string opr, Expression left, Expression right)
    {
        BinaryExpression InnerLambda = null;
        switch (opr)
        {
            case "==":
            case "=":
                InnerLambda = Expression.Equal(left, right);
                break;
            case "<":
                InnerLambda = Expression.LessThan(left, right);
                break;
            case ">":
                InnerLambda = Expression.GreaterThan(left, right);
                break;
            case ">=":
                InnerLambda = Expression.GreaterThanOrEqual(left, right);
                break;
            case "<=":
                InnerLambda = Expression.LessThanOrEqual(left, right);
                break;
            case "!=":
                InnerLambda = Expression.NotEqual(left, right);
                break;
            case "&&":
                InnerLambda = Expression.And(left, right);
                break;
            case "||":
                InnerLambda = Expression.Or(left, right);
                break;
        }
        return InnerLambda;
    }

    public static Expression<Func<T, TResult>> And<T, TResult>(this Expression<Func<T, TResult>> expr1, Expression<Func<T, TResult>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, TResult>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
    }

    public static Func<T, TResult> ExpressionToFunc<T, TResult>(this Expression<Func<T, TResult>> expr)
    {
        var res = expr.Compile();
        return res;
    }
}

LinqPad具有Dump()方法

票数 10
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/821365

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档