• 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

ExpressionTree编译()方法生成一个计算器异常

c/c++ 来源:Gui 5次浏览

我有一个gridview,我们可以在其中筛选不同的标准。每个条件都是一个表达式。我有一个场景,我可以有超过一千个标准,导致我的表达式在调用编译方法时抛出一个StackOverflow。ExpressionTree编译()方法生成一个计算器异常

我仍然是使用Expression btw的初学者。

下面是我重制计算器的示例。

var param = Expression.Parameter(typeof(SomeEntity), "SomeEntity"); 

     Expression finalExpression = Expression.Default(typeof(bool)); 

     for (int i = 0; i < 20000; i++) // Create 20000 expressions 
     { 
      var left = Expression.Property(param, "OrderID"); 
      var right = Expression.Constant(42.ToString()); 

      var expression = BinaryExpression.Equal(left, right); 

      finalExpression = Expression.OrElse(finalExpression, expression); 
     } 

     var hello = Expression.Lambda(finalExpression, param); 
     hello.Compile(); 

我的问题是:有没有办法“减少”这个表达式或任何其他解决方案,防止从stackoverflow?

感谢

注:这里就是表达看起来像调试器:

(SomeEntity.OrderID == "42")) 
OrElse (SomeEntity.OrderID == "42")) 
OrElse (SomeEntity.OrderID == "42")) 
OrElse (SomeEntity.OrderID == "42")) 
OrElse (SomeEntity.OrderID == "42")) 
x20000 


===========解决方案如下:

我刚刚成功地测试此代码高达1,000,000条件下不使用堆栈溢出 – 我怀疑它可以处理多达尽管你想要的条件。

当在lambda表达式上调用Compile时,表达式树被递归地放下来编译它;非常深的树木(像这样)需要很多很多堆栈框架才能完成 – 因此需要StackOverflowException

我在下面所做的是在编译表达式并将其推送到已经生成的条件集合之前,只取得最多固定数量的条件(由MaxPredicateConditionCount设置)。如果预生成的表达式的集合达到最大值,那么这些表达式将被合并为一个新表达式等等。通过这种方式,我们可以限制编译表达式所需的递归深度(通过分段执行)。

public class PredicateBuilder<TParameter> 
{ 
    private const int MaxPredicateConditionCount = 500; 
    private readonly List<Expression<Func<TParameter, bool>>> _existingPredicates = new List<Expression<Func<TParameter, bool>>>(MaxPredicateConditionCount); 
    private readonly ParameterExpression _parameter = Expression.Parameter(typeof(TParameter)); 
    private Expression<Func<TParameter, bool>> _expression; 
    private Expression _workingPredicate; 
    private int _workingPredicateConditionCount; 
    public bool Built { get; private set; } 

    public Expression<Func<TParameter, bool>> LambdaExpression 
    { 
     get 
     { 
      if (!Built) 
      { 
       return null; 
      } 

      return _expression; 
     } 
    } 

    public void AddCondition<TValue>(string propertyName, TValue value) 
    { 
     if (Built) 
     { 
      throw new InvalidOperationException("Predicate has already been built"); 
     } 

     var property = Expression.Property(_parameter, propertyName); 
     var constant = Expression.Constant(value, typeof(TValue)); 
     var equality = Expression.Equal(property, constant); 

     if (_workingPredicate == null) 
     { 
      _workingPredicate = equality; 
     } 
     else 
     { 
      if (MaxPredicateConditionCount < ++_workingPredicateConditionCount) 
      { 
       var compiledWorking = Expression.Lambda<Func<TParameter, bool>>(_workingPredicate, _parameter).Compile(); 
       _existingPredicates.Add(p => compiledWorking(p)); 

       if (_existingPredicates.Count + 1 > MaxPredicateConditionCount) 
       { 
        var compiled = BuildExistingPredicates().Compile(); 
        _existingPredicates.Clear(); 
        _existingPredicates.Add(p => compiled(p)); 
       } 

       _workingPredicate = equality; 
       _workingPredicateConditionCount = 0; 
      } 
      else 
      { 
       _workingPredicate = Expression.OrElse(_workingPredicate, equality); 
      } 
     } 
    } 

    private Expression<Func<TParameter, bool>> BuildExistingPredicates() 
    { 
     Expression compileTemp = Expression.Invoke(_existingPredicates[0], _parameter); 

     for (var i = 1; i < _existingPredicates.Count; ++i) 
     { 
      var nextCall = Expression.Invoke(_existingPredicates[i], _parameter); 
      compileTemp = Expression.OrElse(compileTemp, nextCall); 
     } 

     return Expression.Lambda<Func<TParameter, bool>>(compileTemp, _parameter); 
    } 

    public void Build() 
    { 
     Built = true; 
     //There were no conditions, assume true 
     if (_workingPredicate == null) 
     { 
      _expression = x => true; 
      return; 
     } 

     _existingPredicates.Add(Expression.Lambda<Func<TParameter, bool>>(_workingPredicate, _parameter)); 

     _expression = BuildExistingPredicates(); 

     _existingPredicates.Clear(); 
     _workingPredicate = null; 
     _workingPredicateConditionCount = 0; 
    } 

    public Func<TParameter, bool> Compile() 
    { 
     if (!Built) 
     { 
      Build(); 
     } 

     return _expression.Compile(); 
    } 
} 

例实体

public class SomeEntity 
{ 
    public string OrderID { get; set; } 
} 

用法

class Program 
{ 
    static void Main() 
    { 
     var builder = new PredicateBuilder<SomeEntity>(); 

     for (int i = 0; i < 1000000; i++) // Create 1,000,000 expressions 
     { 
      builder.AddCondition("OrderID", "42"); 
      Console.Title = i.ToString(); 
     } 

     builder.Compile(); 
    } 
} 

版权声明:本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系管理员进行删除。
喜欢 (0)