在我的项目中,我一直在使用Linq's Sum()
。它由MySQL上的NHibernate提供支持。在我的Session Factory
中,我明确要求NHibernate处理decimals
时的8位小数:
public class DecimalsConvention : IPropertyConvention
{
public void Apply(IPropertyInstance instance)
{
if (instance.Type.GetUnderlyingSystemType() == typeof(decimal))
{
instance.Scale(8);
instance.Precision(20);
}
}
}
然而,我发现.Sum()
将小数点后5位的数字四舍五入:
var opasSum = opasForThisIp.Sum(x => x.Amount); // Amount is a decimal
在上面的陈述opaSum
等于 2.46914 ,而它应该是 2.46913578 (直接在MySQL上计算)。 opasForThisIp
的类型为IQueryable<OutgoingPaymentAssembly>
。
对于decimals
,我需要所有Linq计算来处理8个小数位。
有关如何解决此问题的任何想法?
修改1 :我找到了var opasSum = Enumerable.Sum(opasForThisIp, opa => opa.Amount);
来生成正确的结果,但问题仍然存在,为什么.Sum()
对结果进行汇总以及如何解决?< / p>
编辑2 :生成的SQL似乎有问题:
select cast(sum(outgoingpa0_.Amount) as DECIMAL(19,5)) as col_0_0_
from `OutgoingPaymentAssembly` outgoingpa0_
where outgoingpa0_.IncomingPayment_id=?p0
and (outgoingpa0_.OutgoingPaymentTransaction_id is not null);
?p0 = 24 [Type: UInt64 (0)]
编辑3 :var opasSum = opasForThisIp.ToList().Sum(x => x.Amount);
也会产生正确的结果。
修改4 :将IQueryable<OutgoingPaymentAssembly>
转换为IList<OutgoingPaymentAssembly>
,使原始查询:var opasSum = opasForThisIp.Sum(x => x.Amount);
生效。
答案 0 :(得分:2)
x.Amount正在从&#34; LINQ-to-SQL&#34;转换为低精度最小类型。转换,因为您的收藏是IQueryable。
有几种解决方法,最简单的方法是将集合的类型更改为IList,或者调用集合上的ToList(),强制linq查询作为LINQ-to-Objects运行。
var opasSum = opasForThisIp.ToList().Sum(x => x.Amount);
注意:的 如果您不希望通过远离IQueryable而丢失延迟执行,您可以尝试将Amount转换为linq查询中的小数。
来自MSDN decimal and numeric (Transact-SQL):
在Transact-SQL语句中,带小数点的常量是 使用最小值自动转换为数字数据值 精度和规模必要。例如,常数12.345是 转换为精度为5且比例为3的数值。
编辑(包含对不同.NET集合类型的精彩解释:
的答案IQueryable旨在允许查询提供程序(例如, ORM,如LINQ to SQL或Entity Framework)来使用表达式 包含在查询中以将请求转换为另一种格式。在 换句话说,LINQ-to-SQL查看实体上的属性 你正在使用你正在制作和实际比较的比较 创建一个SQL语句来表达(希望)一个等效的请求。
IEnumerable比IQueryable更通用(尽管如此) IQueryable的实例实现IEnumerable)并且只定义 一个序列。但是,有可用的扩展方法 可枚举的类,在其上定义一些查询类型运算符 接口并使用普通代码来评估这些条件。
List只是一种输出格式,并且实现时 IEnumerable与查询没有直接关系。
换句话说,当您使用IQueryable时,您需要定义和 表达式被翻译成其他东西。即使 你正在编写代码,代码永远不会被执行,它只会得到 检查并转换为其他内容,就像实际的SQL查询一样。 因此,只有某些内容在这些内容中有效 表达式。例如,你不能调用普通的函数 你可以在这些表达式中定义,因为LINQ-to-SQL并不是这样 知道如何将您的调用转换为SQL语句。大多数这些 不幸的是,限制仅在运行时进行评估。
当您使用IEnumerable进行查询时,您正在使用 LINQ到对象,这意味着您正在编写实际代码 用于评估您的查询或转换结果,所以有 一般来说,你可以做什么没有限制。你可以打电话 这些表达式中的其他功能可以自由使用。