• 【知识点】IQueryable.SumAsync方法的NULL异常


      进行这样一个查询,统计用户积分(point是int类型字段),代码很简单,结果却报错了:
    1 await DbContext.Set<GetPointDetail>().Where(p => p.OwnerId == User.Id).SumAsync(p => p.Point);
     
      日志显示,查询结果为null,转换为int类型失败,必须使用可空类型。好吧,看到结果为null我就知道问题出在哪了。
      先来看看该行代码生成的sql语句:
    1 exec sp_executesql N'SELECT 
    2     [GroupBy1].[A1] AS [C1]
    3     FROM ( SELECT 
    4         SUM([Extent1].[Point]) AS [A1]
    5         FROM [dbo].[GetPointDetails] AS [Extent1]
    6         WHERE [Extent1].[OwnerId] = @p__linq__0
    7     )  AS [GroupBy1]',N'@p__linq__0 varchar(8000)',@p__linq__0='xxx'
    8 go
      熟悉sql server的应该知道,sum函数是会过忽略NULL值而非作为0参与计算,而如果查询结果没有一条数据,会直接返回NULL,我遇到的正是这种情况。而代码中的IQueryable.SumAsync()返回值是int,就是说,内部会将数据库返回的NULL值转换为int,显然就会报错。
      那么如何修改呢?先看第一种方案,使用DefaultIfEmpty方法:
    1 await DbContext.Set<GetPointDetail>().Where(p => p.OwnerId == User.Id).Select(p => p.Point).DefaultIfEmpty().SumAsync();
     1 exec sp_executesql N'SELECT 
     2     [GroupBy1].[A1] AS [C1]
     3     FROM ( SELECT 
     4         SUM([Join1].[A1]) AS [A1]
     5         FROM ( SELECT 
     6             CASE WHEN ([Project1].[C1] IS NULL) THEN 0 ELSE [Project1].[Point] END AS [A1]
     7             FROM   ( SELECT 1 AS X ) AS [SingleRowTable1]
     8             LEFT OUTER JOIN  (SELECT 
     9                 [Extent1].[Point] AS [Point], 
    10                 cast(1 as tinyint) AS [C1]
    11                 FROM [dbo].[GetPointDetails] AS [Extent1]
    12                 WHERE [Extent1].[OwnerId] = @p__linq__0 ) AS [Project1] ON 1 = 1
    13         )  AS [Join1]
    14     )  AS [GroupBy1]',N'@p__linq__0 varchar(8000)',@p__linq__0='xxx'
    15 go
      DefaultIfEmpty()会对集合元素做空判断,为null就返回默认值,生成的sql表现为case when判断,不过这种sql明显不是一个好sql。
      那么从日志的错误信息入手,日志已经告诉我们,“must use a nullable type”,要使用int的可空类型,所以第二种方案就是对point字段做个“int?”显示转换:
    1 await DbContext.Set<GetPointDetail>().Where(p => p.OwnerId == User.Id).SumAsync(p => (int?)p.Point);
      生成的sql同最初一样。这样无论结果是整数还是NULL,都能正确转换。
     
      提一句,可空值类型也是值类型,两者转换不会进行装箱/拆箱。

  • 相关阅读:
    oracle的网络服务名和监听程序
    ArcGIS 产品许可模型
    老鸟对菜鸟的一些建议(转)
    ajax简介(转载)
    dim redim
    投影选择的一般原则
    *.prj文件
    cup性能对比感受
    treeview 数据库 递归
    简论两句话
  • 原文地址:https://www.cnblogs.com/linys2333/p/11402406.html
Copyright © 2020-2023  润新知