动态生成Linq Select

时间:2015-08-17 19:22:12

标签: c# sql-server linq dynamic

我有一个数据库,用户可以在其上运行各种计算。计算在4个不同的列上运行,每个计算不一定使用每一列,即calculate1可能变为sql,如

SELECT SUM(Column1) 
FROM TABLE 
WHERE Column1 is not null

和计算2将是

SELECT SUM(Column2)
WHERE Column2 is null

我试图通过linq生成这个,我可以通过每次计算所有内容来获取正确的数据,例如

table.Where(x => x.Column1 != null)
     .Where(x => x.Column2 == null)
     .GroupBy(x => x.Date)
     .Select(dateGroup => new
             {
               Calculation1 = dateGroup.Sum(x => x.Column1 != null),
               Calculation2 = dateGroup.Sum(x => x.Column2 == null)
             }

问题是我的数据集非常大,所以除非用户请求,否则我不想执行计算。我已经研究过动态生成Linq查询。到目前为止我发现的只有PredicateBuilder和DynamicSQL,它们似乎只对动态生成Where谓词很有用,并且将sql查询本身硬编码为一个字符串,必要时插入Sum(Column1)或Sum(Column2)。

如何动态地将Select查询的不同部分添加到这样的匿名类型中?或者我应该看一个完全不同的处理方式

2 个答案:

答案 0 :(得分:0)

您可以在不执行查询的情况下返回查询,这将允许您动态选择要返回的内容。

也就是说,您无法在运行时动态修改匿名类型。它们在编译时静态输入。但是,您可以使用不同的返回对象来允许动态属性,而无需外部库。

var query = table
    .Where(x => x.Column1 != null)
    .Where(x => x.Column2 == null)
    .GroupBy(x => x.Date);

然后,您可以使用以下任何一种方式动态解决查询:

  1. dynamic

    dynamic returnObject = new ExpandoObject();
    
    if (includeOne)
        returnObject.Calculation1 = groupedQuery.Select (q => q.Sum(x => x.Column1));
    
    if (includeTwo)
        returnObject.Calculation2 = groupedQuery.Select (q => q.Sum (x => x.Column2));
    
  2. 具体类型

    var returnObject = new StronglyTypedObject();
    if (includeOne)
        returnObject.Calculation1 = groupedQuery.Select (q => q.Sum(x => x.BrandId));
    
  3. Dictionary<string, int>

答案 1 :(得分:0)

我解决了这个问题,并通过使用hacky解决方法使自己不必使用Dynamic Linq失去类型安全性。我有一个包含bools的对象,它对应于我想要做的计算,例如

public class CalculationChecks
{
  bool doCalculation1 {get;set;}
  bool doCalculation2 {get;set;}
}

然后在我的选择中检查我是否应该进行计算或返回常量,如此

Select(x => new 
{
  Calculation1 = doCalculation1 ? DoCalculation1(x) : 0,
  Calculation2 = doCalculation2 ? DoCalculation2(x) : 0
}

然而,这似乎是linq到sql或ef的边缘情况,这导致生成的sql仍然执行DoCalculation1()和DoCalculation2中指定的计算,然后使用case语句来决定它是否转到将数据返回给我。它的运行速度明显较慢,测试结果为40-60%,执行计划显示它使用的查询效率低得多。

此问题的解决方案是使用ExpressionVisitor遍历表达式并在相应的bool为false时删除计算。显示如何实现此ExpressionVisitor的代码由@StriplingWarrior在此问题上提供Have EF Linq Select statement Select a constant or a function

同时使用这两个解决方案仍然没有创建以100%的速度运行的sql。在测试中,无论测试集的大小如何,它都在普通sql的10s内,并且执行计划的主要部分是相同的