Linq Sum() precision
在我的项目中,我经常使用
1 2 3 4 5 6 7 8 9 10 11 | public class DecimalsConvention : IPropertyConvention { public void Apply(IPropertyInstance instance) { if (instance.Type.GetUnderlyingSystemType() == typeof(decimal)) { instance.Scale(8); instance.Precision(20); } } } |
不过,我发现
在上面的声明中,
当涉及到
有什么解决办法吗?
编辑1:我发现
编辑2:生成的SQL似乎有问题:
1 2 3 4 5 | 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:
编辑4:将
x.amount正在从"linq to sql"转换为低精度的最小类型,因为您的集合是可查询的。
有几种解决方法,其中最简单的方法是将集合的类型更改为ilist,或者对集合调用tolist(),强制linq查询作为linq to对象运行。
1 | var opasSum = opasForThisIp.ToList().Sum(x => x.Amount); |
注:如果您不想通过从IQueryable移走而丢失延迟的执行,可以尝试在Linq查询中将金额强制转换为十进制。
来自msdn decimal和numeric(Transact-SQL):
In Transact-SQL statements, a constant with a decimal point is
automatically converted into a numeric data value, using the minimum
precision and scale necessary. For example, the constant 12.345 is
converted into a numeric value with a precision of 5 and a scale of 3.
编辑(包括对不同.NET集合类型的详细说明:
摘自这个问题的答案。
IQueryable is intended to allow a query provider (for example, an
ORM like LINQ to SQL or the Entity Framework) to use the expressions
contained in a query to translate the request into another format. In
other words, LINQ-to-SQL looks at the properties on the entities that
you're using along with the comparisons you're making and actually
creates a SQL statement to express (hopefully) an equivalent request.IEnumerable is more generic than IQueryable (though all
instances of IQueryable implement IEnumerable) and only defines
a sequence. However, there are extension methods available within the
Enumerable class that define some query-type operators on that
interface and use ordinary code to evaluate these conditions.List is just an output format, and while it implements
IEnumerable, is not directly related to querying.In other words, when you're using IQueryable, you're defining and
expression that gets translated into something else. Even though
you're writing code, that code never gets executed, it only gets
inspected and turned into something else, like an actual SQL query.
Because of this, only certain things are valid within these
expressions. For instance, you cannot call an ordinary function that
you define from within these expressions, since LINQ-to-SQL doesn't
know how to turn your call into a SQL statement. Most of these
restrictions are only evaluated at runtime, unfortunately.When you use IEnumerable for querying, you're using
LINQ-to-Objects, which means you are writing the actual code that is
used for evaluating your query or transforming the results, so there
are, in general, no restrictions on what you can do. You can call
other functions from within these expressions freely.