与 Linq-to-Entities 一起使用的可重用函数
Reusable functions for use with Linq-to-Entities
我有一些统计代码,我想在不同的地方使用它来计算计划的成功/失败百分比 Results
。我最近在代码中发现了一个错误,这是因为它在每个 LINQ 语句中都被复制了,然后我决定最好使用通用代码来执行此操作。当然,问题是在 SQL 服务器上执行时,正常函数会抛出 NotSupportedException
,因为 SQL 服务器中不存在该函数。
如何编写在 SQL 服务器上执行的可重用统计代码,或者这不可能吗?
这是我为 Result
编写的代码
public class Result
{
public double CalculateSuccessRatePercentage()
{
return this.ExecutedCount == 0 ? 100 : ((this.ExecutedCount - this.FailedCount) * 100.0 / this.ExecutedCount);
}
public double CalculateCoveragePercentage()
{
return this.PresentCount == 0 ? 0 : (this.ExecutedCount * 100.0 / this.PresentCount);
}
}
并这样使用(结果为IQueryable
,并抛出异常):
schedule.SuccessRatePercentage = (int)Math.Ceiling(results.Average(r => r.CalculateSuccessRatePercentage()));
schedule.CoveragePercentage = (int)Math.Ceiling(results.Average(r => r.CalculateCoveragePercentage()));
或像这样(有效,因为我们对单个结果执行此操作)
retSchedule.SuccessRatePercentage = (byte)Math.Ceiling(result.CalculateSuccessRatePercentage());
retSchedule.CoveragePercentage = (byte)Math.Ceiling(result.CalculateCoveragePercentage());
编辑
根据@Fred 的回答,我现在有以下代码,适用于 IQueryable
schedule.SuccessRatePercentage = (int)Math.Ceiling(scheduleResults.Average(ScheduleResult.CalculateSuccessRatePercentageExpression()));
schedule.CoveragePercentage = (int)Math.Ceiling(scheduleResults.Average(ScheduleResult.CalculateCoveragePercentageExpression()));
唯一的问题是此代码不适用于个别结果,即
retSchedule.SuccessRatePercentage = (byte)Math.Ceiling(/* How do I use it here for result */);
您不能将函数传递给 SQL - 您需要在实际的 SQL 数据库上声明该函数,然后从您的代码中调用它。
你可以 do/try 是这样的:
Expression<Func<Result, double>> CalculateCoveragePercentage()
{
return r => r.PresentCount == 0 ? 0 : (r.ExecutedCount * 100.0 / r.PresentCount);
}
需要解释而不是执行,以便 EF 可以将其转换为 SQL。问题是,我只听说过将它直接传递到 where
子句中是可能的。
因为当你直接在你的 LINQ 查询中应用这些计算时你能够进行这些计算,我倾向于认为也应该可以将这些计算声明为 Expression<Func<..., ...>>
并且它们传递它们英寸
唯一可以确定的方法就是尝试(除非您想查看 EF 的 ExpressionBuilder)
更新:
我应该提到,如果这可行,您需要将此表达式传递到 Select
语句中:
// Assuming you have Results declared as a DbSet or IDbSet, such as:
DbSet<Result> Results
// You could do something like this (just to illustrate that
// it would be interpreted rather than executed):
List<double> allCoveragePercentages = Results.Select(CalculateCoveragePercentage)
.ToList();
更新 #2:
为了使其适用于单个结果(或任何情况下),您需要将其传递到接受表达式的子句中。示例是 Select
、Where
、Average
(显然),任何 而不是 return 的结果。
从我的头顶开始(我确定我遗漏了一些):
列表:ToArray
、ToDictionary
、ToList
、ToLookup
单个结果:First
、FirstOrDefault
、Single
、SingleOrDefault
、Last
、LastOrDefault
计算:Count
、Sum
、Max
、Min
由于上述条款 return 结果,他们(据我所知)只接受 Predicates
(一个只能 return 'true' 或 'false')
您可能恰巧用 .Average(CalculateCoveragePercentage)
做对了
因此,如果您要使用 .FirstOrDefault()
获得单个结果,您将在 select 子句之前的 select 子句中传递表达式:.Select(CalculateCoveragePercentage).FirstOrDefault()
。也就是说,如果您不需要实际实体而只需要计算。请注意,如果没有 Result
结果,此特定示例将 return 0
。您可能想要也可能不想要这种行为。
当然,如果您已经有了结果(它不再是 IQueryable),那么您可以简单地执行以下操作:
var coveragePercentage = CalculateCoveragePercentage().Compile().Invoke(result);
但这会有点违背表达式的目的 - 对于这种情况,您应该在 Result
class 中添加一个方法来计算给定实例的 CoveragePercentage。
我有一些统计代码,我想在不同的地方使用它来计算计划的成功/失败百分比 Results
。我最近在代码中发现了一个错误,这是因为它在每个 LINQ 语句中都被复制了,然后我决定最好使用通用代码来执行此操作。当然,问题是在 SQL 服务器上执行时,正常函数会抛出 NotSupportedException
,因为 SQL 服务器中不存在该函数。
如何编写在 SQL 服务器上执行的可重用统计代码,或者这不可能吗?
这是我为 Result
public class Result
{
public double CalculateSuccessRatePercentage()
{
return this.ExecutedCount == 0 ? 100 : ((this.ExecutedCount - this.FailedCount) * 100.0 / this.ExecutedCount);
}
public double CalculateCoveragePercentage()
{
return this.PresentCount == 0 ? 0 : (this.ExecutedCount * 100.0 / this.PresentCount);
}
}
并这样使用(结果为IQueryable
,并抛出异常):
schedule.SuccessRatePercentage = (int)Math.Ceiling(results.Average(r => r.CalculateSuccessRatePercentage()));
schedule.CoveragePercentage = (int)Math.Ceiling(results.Average(r => r.CalculateCoveragePercentage()));
或像这样(有效,因为我们对单个结果执行此操作)
retSchedule.SuccessRatePercentage = (byte)Math.Ceiling(result.CalculateSuccessRatePercentage());
retSchedule.CoveragePercentage = (byte)Math.Ceiling(result.CalculateCoveragePercentage());
编辑
根据@Fred 的回答,我现在有以下代码,适用于 IQueryable
schedule.SuccessRatePercentage = (int)Math.Ceiling(scheduleResults.Average(ScheduleResult.CalculateSuccessRatePercentageExpression()));
schedule.CoveragePercentage = (int)Math.Ceiling(scheduleResults.Average(ScheduleResult.CalculateCoveragePercentageExpression()));
唯一的问题是此代码不适用于个别结果,即
retSchedule.SuccessRatePercentage = (byte)Math.Ceiling(/* How do I use it here for result */);
您不能将函数传递给 SQL - 您需要在实际的 SQL 数据库上声明该函数,然后从您的代码中调用它。
你可以 do/try 是这样的:
Expression<Func<Result, double>> CalculateCoveragePercentage()
{
return r => r.PresentCount == 0 ? 0 : (r.ExecutedCount * 100.0 / r.PresentCount);
}
需要解释而不是执行,以便 EF 可以将其转换为 SQL。问题是,我只听说过将它直接传递到 where
子句中是可能的。
因为当你直接在你的 LINQ 查询中应用这些计算时你能够进行这些计算,我倾向于认为也应该可以将这些计算声明为 Expression<Func<..., ...>>
并且它们传递它们英寸
唯一可以确定的方法就是尝试(除非您想查看 EF 的 ExpressionBuilder)
更新:
我应该提到,如果这可行,您需要将此表达式传递到 Select
语句中:
// Assuming you have Results declared as a DbSet or IDbSet, such as:
DbSet<Result> Results
// You could do something like this (just to illustrate that
// it would be interpreted rather than executed):
List<double> allCoveragePercentages = Results.Select(CalculateCoveragePercentage)
.ToList();
更新 #2:
为了使其适用于单个结果(或任何情况下),您需要将其传递到接受表达式的子句中。示例是 Select
、Where
、Average
(显然),任何 而不是 return 的结果。
从我的头顶开始(我确定我遗漏了一些):
列表:
ToArray
、ToDictionary
、ToList
、ToLookup
单个结果:
First
、FirstOrDefault
、Single
、SingleOrDefault
、Last
、LastOrDefault
计算:
Count
、Sum
、Max
、Min
由于上述条款 return 结果,他们(据我所知)只接受 Predicates
(一个只能 return 'true' 或 'false')
您可能恰巧用 .Average(CalculateCoveragePercentage)
因此,如果您要使用 .FirstOrDefault()
获得单个结果,您将在 select 子句之前的 select 子句中传递表达式:.Select(CalculateCoveragePercentage).FirstOrDefault()
。也就是说,如果您不需要实际实体而只需要计算。请注意,如果没有 Result
结果,此特定示例将 return 0
。您可能想要也可能不想要这种行为。
当然,如果您已经有了结果(它不再是 IQueryable),那么您可以简单地执行以下操作:
var coveragePercentage = CalculateCoveragePercentage().Compile().Invoke(result);
但这会有点违背表达式的目的 - 对于这种情况,您应该在 Result
class 中添加一个方法来计算给定实例的 CoveragePercentage。