在Linq to Entities中是否有一种有效的中间计算方法

时间:2011-11-17 12:27:10

标签: c# linq entity-framework linq-to-entities refactoring

我有这个在幕后使用LinqToEntities的查询。

(...)
.GroupBy(x => x.FahrerID)
.Select(x => new FahrerligaEintrag()
{
    FahrerID = x.Key,
    FahrerFullName = string.Empty,
    VollgasKmAufHundertKm = (100m / x.Sum(y => y.BasisMeter)) * (x.Sum(y => y.Gas100ProzentInMeter.Value) + x.Sum(y => y.Gas90ProzentInMeter.Value)),
    LeerlaufInProzent = (100m / x.Sum(y => y.BasisSekunden)) * x.Sum(y => y.LeerlaufInSekunden.Value),
    VerbrauchLiterAufHundertKm = (100m / x.Sum(y => y.BasisMeter)) * x.Sum(y => y.VerbrauchInLiter.Value) * 1000,
    RollenKmAufHundertKm = (100m / x.Sum(y => y.BasisMeter)) * x.Sum(y => y.RollenInMeter.Value),
    TempomatKmAufHundertKm = (100m / x.Sum(y => y.BasisMeter)) * x.Sum(y => y.TempomatInMeter.Value),
    GeschwindigkeitsuebertretungenAnzahlAufHundertKm = (100m / x.Sum(y => y.BasisMeter)) * 1000 * x.Sum(y => y.UebertretungenAnzahl.Value),
    GangwechselAnzahlAufHundertKm = (100m / x.Sum(y => y.BasisMeter)) * 1000 * x.Sum(y => y.GangwechselAnzahl.Value)
});

如您所见,此部分重复多次(100m / x.Sum(y => y.BasisMeter))

在Linq to Objects中,首先投射到匿名类来计算因子以避免重复计算是很自然的。像这样:

.GroupBy(x => x.FahrerID)
.Select(x => new
{
    Grouping = x,
    BasisMeterFaktor = 100m / x.Sum(y => y.BasisMeter),
    BasisSekundenFaktor = 100m /x.Sum(y => y.BasisSekunden)
})
.Select(x => new FahrerligaEintrag()
{
    FahrerID = x.Grouping.Key,
    FahrerFullName = string.Empty,
    VollgasKmAufHundertKm = x.BasisMeterFaktor * (x.Grouping.Sum(y => y.Gas100ProzentInMeter.Value) + x.Grouping.Sum(y => y.Gas90ProzentInMeter.Value)),
    LeerlaufInProzent = x.BasisSekundenFaktor * x.Grouping.Sum(y => y.LeerlaufInSekunden.Value),
    VerbrauchLiterAufHundertKm = x.BasisMeterFaktor * x.Grouping.Sum(y => y.VerbrauchInLiter.Value) * 1000,
    RollenKmAufHundertKm = x.BasisMeterFaktor * x.Grouping.Sum(y => y.RollenInMeter.Value),
    TempomatKmAufHundertKm = x.BasisMeterFaktor * x.Grouping.Sum(y => y.TempomatInMeter.Value),
    GeschwindigkeitsuebertretungenAnzahlAufHundertKm = x.BasisMeterFaktor * 1000 * x.Grouping.Sum(y => y.UebertretungenAnzahl.Value),
    GangwechselAnzahlAufHundertKm = x.BasisMeterFaktor * 1000 * x.Grouping.Sum(y => y.GangwechselAnzahl.Value)
});

但是,在LinqToEntities中,这会导致SQL代码性能下降。至少我使用的这个Oracle后端(我无法通过配置文件向我显示SQL)。所以,我想知道是否有另一种方法可以避免重复计算,或者这是否是我能获得的最快速度。

请原谅所有德国变量名称。我相信你仍然有意义。

更新

我能够按照建议使用ToTraceString()。有趣的是,对于投影,SQL包含18(!!!)SELECT语句。没有它,它只包含2.

3 个答案:

答案 0 :(得分:2)

查询上执行的.ToTraceString()是否提供了可以分析的SQL查询?在所有这些计算中很容易迷失,但我敢肯定,如果你想在单个查询中进行所有这些计算,性能将会受到影响。减少重复计算的另一种方法是使用let关键字(没有扩展方法,所以你必须使用“传统”LINQ)。这个“允许”您指定一个可以在查询中重用的变量。但我怀疑它会比你的分组方法更好。

from f in Fahrer
let meterFaktor = 100m / x.Sum(y =>.BasisMeter)
select new FahrerLigaEintrag()
{
    ...
}

答案 1 :(得分:1)

以为我会在这里添加这个,而不仅仅是在Twitter上直接给Christoph。我认为LINQ要翻译的内容很多。是的,它可以完成,但正如他所看到的,它并不漂亮,因为LINQ to Entities必须使用通用算法来处理你抛出的任何东西。如果他有可能将存储过程或视图添加到数据库中,我会转而使用该路径。

我还推荐了(140个字符中最好的),他查看了EFProfiler(efprof.com)或LLBLGen的新分析器(llblgen.com),以便在Oracle中分析EF查询。

答案 2 :(得分:0)

使用通用委托Func<Tx, Ty, TResult>怎么样?

// declare out of query and provide valid types for x, y, result
Func<TX, TY, TResult> basisMeterFaktorAlgo;

// set formula once
basisMeterFaktorAlgo = (x, y) => 100m / x.Sum(y => y.BasisMeter);

// call it in query
basicMeterFactor = basisMeterFaktorAlgo(xValue, yValue)