Linq Sum() precision

本文关键字:precision Sum Linq | 更新日期: 2023-09-27 18:01:40

在我的项目中,我一直在使用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() round up结果,我们如何修复它?

Edit 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)]

Edit 3: var opasSum = opasForThisIp.ToList().Sum(x => x.Amount);也产生正确的结果。

Edit 4:将IQueryable<OutgoingPaymentAssembly>转换为IList<OutgoingPaymentAssembly>使原始查询:var opasSum = opasForThisIp.Sum(x => x.Amount);工作

Linq Sum() precision

Amount从"LINQ-to-SQL"转换为低精度最小类型,因为您的集合是IQueryable。

有几种解决方法,其中最简单的是将集合的类型更改为IList,或者在集合上调用ToList(),强制linq查询作为linq -to- objects运行。

var opasSum = opasForThisIp.ToList().Sum(x => x.Amount);

注意:如果你不想因为离开IQueryable而失去延迟执行,你可以尝试将Amount转换为linq查询内部的小数。

从MSDN的十进制和数字(Transact-SQL):

在Transact-SQL语句中,带有小数点的常量为使用最小值自动转换为数字数据值精度和比例是必要的。例如,常数12.345是转换为精度为5,比例为3的数值。

编辑(包括不同的。net集合类型的伟大解释:

选自这个问题的答案

IQueryable旨在允许查询提供者(例如,一个ORM(如LINQ to SQL或实体框架)来使用表达式包含在查询中以将请求转换为另一种格式。在换句话说,LINQ-to-SQL查看实体上的属性你在使用你所做的比较,实际上创建一个SQL语句来表达(希望)一个等价的请求。

IEnumerable比IQueryable更通用IQueryable的实例实现IEnumerable)并且只定义一个序列。类中提供了一些扩展方法枚举类,在其上定义一些查询类型操作符接口,并使用普通代码计算这些条件。

List只是一个输出格式,当它实现可枚举,与查询没有直接关系。

换句话说,当你使用IQueryable时,你定义了and被翻译成其他东西的表达。尽管你在写代码,代码永远不会被执行,它只会检查并转换为其他内容,例如实际的SQL查询。正因为如此,只有特定的东西是有效的表达式。例如,你不能调用一个普通的函数因为LINQ-to-SQL不会在这些表达式中定义知道如何将调用转换为SQL语句。其中大部分遗憾的是,限制只在运行时评估。

当您使用IEnumerable进行查询时,您正在使用LINQ-to-Objects,这意味着您正在编写实际的代码用于计算查询或转换结果一般来说,对你能做的事情没有限制。你可以打电话