DefaultIfEmpty().Max()仍然会抛出“Sequence contains no elements.”异常。

11

我更新了我的项目到dotnet core 3.0RC1(也可能是preview9),但我的原本可用的代码无法正常工作。

var value = context.Products.Where(t => t.CategoryId == catId).Select(t => t.Version).DefaultIfEmpty().Max();

出现了System.InvalidOperationException: Sequence contains no elements异常。受影响的表为空。

如果我添加ToList(),使代码如下:DeafultIfEmpty().ToList().Max(),则代码重新运行正常。没有找到任何关于破坏性更改的信息。 当我运行时

var expectedZero = new List<int>().DefaultIfEmpty().Max();

它能够正常工作。这让我觉得可能是EF Core出了问题。然后我在xUnit中创建了一个测试,与完全相同的设置,但是测试通过了(表也为空,使用InMemoryDatabase而不是实时SQL Server实例)。

我真的很困惑。 相关的堆栈跟踪:

System.InvalidOperationException: Sequence contains no elements.
   at int lambda_method(Closure, QueryContext, DbDataReader, ResultContext, int[], ResultCoordinator)
   at bool Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor+QueryingEnumerable<T>+Enumerator.MoveNext()
   at TSource System.Linq.Enumerable.Single<TSource>(IEnumerable<TSource> source)
   at TResult Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute<TResult>(Expression query)
   at TResult Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute<TResult>(Expression expression)
   at TSource System.Linq.Queryable.Max<TSource>(IQueryable<TSource> source)
   at ... (my method that run the code)

编辑

产品类别:

   [Table("tmpExtProduct", Schema = "ext")]
    public partial class Product
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int Version { get; set; }

        [Column(TypeName = "datetime")]
        public DateTime ImportDate { get; set; }

        public int CategoryId { get; set; }
        public string Description { get; set; }

        [ForeignKey(nameof(Ext.Category))]
        public int CategoryId { get; set; }        

        [InverseProperty(nameof(Ext.Category.Products))]
        public virtual Category Category { get; set; }
    }

第二次编辑 EF Core生成的SQL

exec sp_executesql N'SELECT MAX([t0].[Version])
FROM (
    SELECT NULL AS [empty]
) AS [empty]
LEFT JOIN (
    SELECT [p].[Version], [p].[CategoryId], [p].[ImportDate], [p].[Description]
    FROM [ext].[tmpExtProduct] AS [p]
    WHERE (([p].[CategoryId] = @__categoryId_0) AND @__categoryId_0 IS NOT NULL)
) AS [t0] ON 1 = 1',N'@__categoryId_0 int',@__categoryId_0=5

你能展示一下Product类的代码吗?特别是Version属性? - MichaelM
1
按照要求,但版本只是一个整数(同时也是主键)。之前没有问题。现在检查.DefaultIfEmpty()的结果返回Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable<int>,其中包含一个类型为int的IEnumerable,其Results View包含1个项目= 0。 - mikeyy
好的,我只是再确认一下它是否可为空。我想我的下一个请求是查看EF Core生成的SQL,因为那里可能有一些额外的线索。否则,我建议在GitHub存储库上开启一个问题,看看是否可以在那里找到解决方案。我可以看到最近一个月才添加了对默认值为空的InMemory支持,所以这可能会影响到这里。 - MichaelM
已发布,但我没有看到任何异常情况... - mikeyy
3个回答

6
所以我打开了EF Core存储库中的问题,并得到了答案。显然这是当前的行为,可能会在以后改变。
建议使用以下方法。
var valueFail = context.Products.Where(t => t.CategoryId == catId)
                .GroupBy(e => 1)
                .Select(t => t.Max(e => e.Version))
                .ToList()[0];

这比我的解决方案 DeafultIfEmpty().ToList().Max() 更好,因为它将在服务器端完成所有工作,而我的解决方案将在客户端计算Max()。


2

一个解决方法可能是使用 OrderByFirstOrDefault 的组合。

var value = context.Products
    .Where(t => t.CategoryId == catId)
    .OrderByDescending(t => t.Version)
    .Select(t => t.Version)
    .FirstOrDefault();

我通常使用这种方法,很少使用Max()


这就是我在寻找的东西。简单而甜美。 - undefined

0

通过mikeyy的自我回答,仍然会抛出异常,但是异常消息变成了“可空对象必须有一个值”。因为当序列为空时,返回值为null,但是null不能赋给值类型int

正确的做法是将目标值类型强制转换为Nullable<>,这样当序列为空时,将返回null而不是抛出“序列不包含任何元素”或“可空对象必须有一个值”的异常。

var value = context.Products
    .Where(t => t.CategoryId == catId)
    .Select(t => (int?)t.Version)
    .Max();

现在,变量value的类型是Nullable<int>,请记得检查它是否为空。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接