C# 动态生成Linq选择

C# 动态生成Linq选择,c#,sql-server,linq,dynamic,C#,Sql Server,Linq,Dynamic,我有一个数据库,用户可以运行各种计算。计算在4个不同的列上运行每个计算不一定使用每个列,即calculation1可能会变成类似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)

我有一个数据库,用户可以运行各种计算。计算在4个不同的列上运行每个计算不一定使用每个列,即calculation1可能会变成类似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查询的不同部分动态添加到这样的匿名类型中?或者我应该寻找一种完全不同的方法来处理这个问题

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

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

var query = table
    .Where(x => x.Column1 != null)
    .Where(x => x.Column2 == null)
    .GroupBy(x => x.Date);
然后,您可以使用以下任意一项动态解析查询:

  • 动态

    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));
    
  • 混凝土类型

    var returnObject = new StronglyTypedObject();
    if (includeOne)
        returnObject.Calculation1 = groupedQuery.Select (q => q.Sum(x => x.BrandId));
    
  • 字典


  • 我解决了这个问题,并通过使用一个黑客解决方案,避免了自己在使用动态Linq时失去类型安全性。我有一个包含布尔的对象,它对应于我想要做的计算,比如

    public class CalculationChecks
    {
      bool doCalculation1 {get;set;}
      bool doCalculation2 {get;set;}
    }
    
    然后在我的select中检查是否应该进行计算或返回一个常量,如下所示

    Select(x => new 
    {
      Calculation1 = doCalculation1 ? DoCalculation1(x) : 0,
      Calculation2 = doCalculation2 ? DoCalculation2(x) : 0
    }
    
    但是,这似乎是linq to sql或ef的一个边缘情况,这导致生成的sql仍然执行DoCalculation1()和DoCalculation2中指定的计算,然后使用case语句决定是否将数据返回给我。它的运行速度要慢得多,测试速度要慢40-60%,执行计划显示它使用的查询效率要低得多

    这个问题的解决方案是使用ExpressionVisitor遍历表达式,如果相应的bool为false,则删除计算。@StriplingWarrior在这个问题上提供了显示如何实现这个ExpressionVisitor的代码


    同时使用这两种解决方案仍然无法创建运行速度为普通sql 100%的sql。在测试过程中,无论测试集的大小,它都是在普通sql的10秒内完成的,并且执行计划的主要部分是相同的

    而不是动态创建查询,您能将它们封装到它们自己的方法中并有条件地调用这些方法吗?看看动态LINQ:。它允许在许多LINQ操作中使用动态子句,包括
    Select
    。我看了更多关于你的链接,但摆脱类型安全似乎是我唯一想做的选择,如果我必须要看看如果我可以分别进行每个计算,那将如何工作。问题是,我必须进行不同的计算分组,直到用户请求进来,我才知道。我曾想过对所有可能的组合进行硬编码,但由于已经有14种可能的计算,可能还有更多,因此不可行。你的建议有没有办法解决这个问题?我想可能有,但如果你能描述一下请求是如何产生的,那会很有帮助。您有一个示例吗?当前,当后端获取请求列表时,有几个参数应用于每个计算,如report date,这些参数在where子句中完成,并按照您演示的方式分组。然后是计算名称列表和获取所选计算数据的switch语句。问题是,为了在switch语句中提供这些数据,我需要Linq已经计算了所有内容。我找不到一种方法来动态执行不同的Select语句,而不诉诸动态LinqIt中的硬编码字符串。结果是,对于一个用户,我可能希望Select是
    Select(x=>new{calculation1=Sum(x.column1),calculation2=Count(x=>x.column2>0)}
    但是对于另一个用户,我希望选择是
    Select(x=>new{calculation3=Sum(x.column2)})
    这仅适用于14种不同的计算,用户可以选择从0到所有14种计算的任意位置run@AlexanderBurke听起来您需要一个更丰富的系统来选择用户如何选择查询以及如何构建这些查询。这是可能的,尽管我怀疑在这一过程中,你会失去相当多的打字能力,因为你可能需要构建一些非常动态的东西。也就是说,根据你迄今为止给出的答案,很难真正改进我的答案并使其对你更有用。