Out of Memory Lambda Compile与内联委托

时间:2014-12-10 14:59:44

标签: c# performance linq lambda expression-trees

将4.5.1与服务器端的应用程序一起使用,同时将图表数据与许多REST请求混合。

使用IQueryable构建查询。例如,我最初具有以下内容:

 var query = ctx.Respondents
   .Join(
     ctx.Respondents,
     other => other.RespondentId,
     res => res.RespondentId,
     (other, res) => new ChartJoin { Respondent = res, Occasion = null, BrandVisited = null, BrandInfo = null, Party = null, Item = null }
   )
   . // bunch of other joins filling out the ChartJoin
   .Where(x => x.Respondent.status == 1)
   . // more Where clauses dynamically applied
   .GroupBy(x => new CommonGroupBy { Year = (int)x.Respondent.currentVisitYear, Month = (int)x.Respondent.currentVisitMonth })
   .OrderBy(x => x.Key.Year)
   .ThenBy(x => x.Key.Month)
   .Select(x => new AverageEaterCheque
     {
       Year = x.Key.Year,
       Month = x.Key.Month,
       AverageCheque = (double)(x.Sum(m => m.BrandVisited.DOLLAR_TOTAL) / x.Sum(m => m.BrandVisited.NUM_PAID)),
       Base = x.Count(),
       Days = x.Select(m => m.Respondent.visitDate).Distinct().Count()
   });

为了允许动态分组(通过客户端),GroupBy是使用返回Dictionary的C#表达式生成的。 Select也必须使用表达式生成。上面的Select变成了:

 public static Expression<Func<IGrouping<IDictionary<string, object>, ChartJoin>, AverageEaterCheque>> GetAverageEaterChequeSelector()
 {
    // x => 
    var ParameterType = typeof(IGrouping<IDictionary<string, object>, ChartJoin>);
    var parameter = Expression.Parameter(ParameterType);

    // x => x.Sum(m => m.BrandVisited.DOLLAR_TOTAL) / x.Sum(m => m.BrandVisited.NUM_PAID)
    var m = Expression.Parameter(typeof(ChartJoin), "m");

    var mBrandVisited = Expression.PropertyOrField(m, "BrandVisited");

    PropertyInfo DollarTotalPropertyInfo = typeof(BrandVisited).GetProperty("DOLLAR_TOTAL");
    PropertyInfo NumPaidPropertyInfo = typeof(BrandVisited).GetProperty("NUM_PAID");

    ....

    return a lambda...
 }

当我在本地进行测试运行时,出现Out of Memory错误。然后我开始阅读Totin和Lambda编译的其他人的博客,表达树一般都很昂贵。不知道它会破坏我的申请。我需要能够动态添加分组,这导致我使用表达式树作为GroupBy和Select子句。

是否会喜欢关于如何在应用程序中追踪内存违规者的一些指示?已经看到有些人使用dotMemory,但也有一些实用技巧。监控C#,DotNet的经验很少。

1 个答案:

答案 0 :(得分:4)

由于您正在将表达式编译为委托,因此使用LINQ to Objects执行操作,而不是使用IQueryable重载。这意味着整个数据集被拉入内存,并且所有处理都由应用程序完成,而不是在数据库中完成,只有最终结果被发送到应用程序。

显然将整个表格拉入内存就足以让你的应用程序无法运行。

您需要编译lambda,并将其保留为表达式,从而允许查询提供程序将其转换为SQL,就像使用原始代码一样。