我有一个数据库,用户可以在其上运行各种计算。计算在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查询的不同部分添加到这样的匿名类型中?或者我应该看一个完全不同的处理方式
答案 0 :(得分:0)
您可以在不执行查询的情况下返回查询,这将允许您动态选择要返回的内容。
也就是说,您无法在运行时动态修改匿名类型。它们在编译时静态输入。但是,您可以使用不同的返回对象来允许动态属性,而无需外部库。
var query = table
.Where(x => x.Column1 != null)
.Where(x => x.Column2 == null)
.GroupBy(x => x.Date);
然后,您可以使用以下任何一种方式动态解决查询:
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));
具体类型
var returnObject = new StronglyTypedObject();
if (includeOne)
returnObject.Calculation1 = groupedQuery.Select (q => q.Sum(x => x.BrandId));
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内,并且执行计划的主要部分是相同的