如何在聚合之前将值转换为 long?
Posted
技术标签:
【中文标题】如何在聚合之前将值转换为 long?【英文标题】:How to cast a value to long before aggregating it? 【发布时间】:2019-04-08 18:04:16 【问题描述】:从 EF 核心 2.1.4 开始,如果我们在聚合之前将 int
值转换为 long
或 long?
(可能是为了避免算术溢出),此转换不会影响生成的查询,并且无论如何都会发生溢出.
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
namespace EfCoreBugs
class Program
static void Main(string[] args)
using (var dbContext = new MyDbContext())
Console.WriteLine(dbContext.Payments.Sum(x => (long?)x.Amount));
Console.ReadLine();
public class MyDbContext : DbContext
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
optionsBuilder.UseSqlServer(@"Server=.;Database=EfCoreBugs;Trusted_Connection=True;MultipleActiveResultSets=True;");
public DbSet<Payment> Payments get; set;
public class Payment
public int Id get; set;
public int Amount get; set;
生成的查询是:
SELECT SUM([x].[Amount])
FROM [Payments] AS [x]
有没有办法解决这个溢出问题? (除了把Amount
的数据类型改成long
)
【问题讨论】:
dbContext.Payments.Select(x => (long)x.Amount).Sum();
?这行得通吗,只是猜测
@TheGeneral 不,仍然会创建相同的查询。我也试过:dbContext.Payments.Select(x => new A = (long?)x.Amount ).Sum(z => z.A)
失败了。
也可以试试Convert.ToInt64(x.Amount)
它可能会也可能不会起作用
当然,一种解决方法是在数据库之外进行求和(当然可能对性能产生严重影响),如dbContext.Payments.Select(x => x.Amount).AsEnumerable().Sum(x => (long?)x)
。但我认为您希望在数据库上完成总和。
@TheGeneral Convert.ToInt64(x.Amount)
转换为 SELECT SUM(CONVERT(bigint, [x].[Amount])) FROM [Payments] AS [x]
并且运行时不会溢出。你能把它写成答案吗?
【参考方案1】:
试试Convert.ToInt64(x.Amount)
它可以翻译成
SELECT SUM(CONVERT(bigint, [x].[Amount])) FROM [Payments] AS [x]
并运行而不会溢出
为未来的读者。这实际上取决于 ORM,它可能并非在所有情况下都有效
【讨论】:
只是出于好奇:如果[Payments]
表不包含任何行(或者如果[Amount] is null
在所有行中,对于允许为空的列)生成的SQL 将产生null
,而 C# 变量的类型为 long
,不支持 null
。在这种情况下,你会在 C# 中看到 0
吗?
是的,但它仍然很有趣。在 .NET Framework 本身中,Convert.ToInt64((object)null)
将成功并返回 0L
。如果您将 int?
和 HasValue
false 放入 Convert.ToInt64(·)
,则使用此重载。但是我这里没有 EF,所以我没有测试那里发生了什么。
@JeppeStigNielsen 是的,我在手机上,所以我也无法测试它以上是关于如何在聚合之前将值转换为 long?的主要内容,如果未能解决你的问题,请参考以下文章