Dynamically generate Linq Select

夙愿已清 提交于 2019-12-13 14:21:59

问题


I have a database that users can run a variety of calculations on. The calculations run on 4 different columns each calculation does not necessarily use every column i.e. calculation1 might turn into sql like

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

and calculation2 would be

SELECT SUM(Column2)
WHERE Column2 is null

I am trying to generate this via linq and I can get the correct data back by calculating everything every time such as

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)
             }

The problem is that my dataset is very large, and so I do not want to perform a calculation unless the user has requested it. I have looked into dynamically generating Linq queries. All I have found so far is PredicateBuilder and DynamicSQL, which appear to only be useful for dynamically generating the Where predicate, and hardcoding the sql query itself as a string with the Sum(Column1) or Sum(Column2) being inserted when necessary.

How would one go about dynamically adding the different parts of the Select query into an anonymous type like this? Or should I be looking at an entirely different way of handling this


回答1:


You can return your query without executing it, which will allow you to dynamically choose what to return.

That said, you cannot dynamically modify an anonymous type at runtime. They are statically typed at compile time. However, you can use a different return object to allow for dynamic properties without needing an external library.

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

You can then dyamically resolve queries with any one of the following:

  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. Concrete Type

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




回答2:


I solved this and kept myself from having to lose type safety with Dynamic Linq by using a hacky workaround. I have a object containing bools that correspond to what calculations I want to do such as

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

and then do a check in my select for whether or not I should do the calculation or return a constant, like so

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

However, this appears to be an edge case with linq to sql or ef, that causes the generated sql to still do the calculations specified in DoCalculation1() and DoCalculation2 and then use a case statement to decide whether or not its going to return the data to me. It runs signficantly slower,40-60% in testing, and the execution plan shows that it uses a much more inefficient query.

The solution to this problem was to use an ExpressionVisitor to go through the expression and remove the calculations if the corresponding bool was false. The code showing how implement this ExpressionVisitor was provided by @StriplingWarrior on this question Have EF Linq Select statement Select a constant or a function

Using both of these solutions together is still not creating sql that runs at 100% the speed of plain sql. In testing it was within 10s of plain sql no matter the size of the test set, and the major portions of the execution plan were the same



来源:https://stackoverflow.com/questions/32058114/dynamically-generate-linq-select

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!