我在我的asp core API项目中使用ef core。 我必须找到最高顺序的索引。
示例:
数据表:Id, ForeignId, OrderIndex
所以我在做:
var highestOrderIndex = await _context
.ExampleDbSet
.Where(x =>
x.ForeignId == foreignId)
.MaxAsync(x =>
x.OrderIndex);
问题在于示例数据库集包含0个元素时。这将引发异常:Sequence contains no element
。
是否有一种优雅的方法?因为我不想从数据库中获取所有元素。而且应该是异步的。
谢谢
答案 0 :(得分:6)
实际上,利用诸如Min
,Max
之类的聚合方法抛出的事实,这是一种相当优雅的方法(与其他答案中建议的相比,性能更高,因为它仅执行单个数据库查询) Sequence contains no element
仅在与不可为空的重载一起使用时例外,但可为空的重载只是返回null
。
因此,您所需要做的就是将不可为空的属性类型提升为相应的可为空类型。例如,如果OrderIndex
类型为int
,则对查询的唯一更改可能是
.MaxAsync(x => (int?)x.OrderIndex);
请注意,这还将把接收变量highestOrderIndex
的类型更改为int?
。您可以检查null
并做出相应的反应,也可以简单地将聚合函数调用与??
运算符结合起来并提供一些默认值,例如
var highestOrderIndex = (await _context.ExampleDbSet
.Where(x => x.ForeignId == foreignId)
.MaxAsync(x => (int?)x.OrderIndex)) ?? -1; // or whatever "magic" number works for you
答案 1 :(得分:3)
您可以查找是否存在任何记录,如果存在,则查找最大值。像这样:
var query = _context.ExampleDbSet
.Where(x => x.ForeignId == foreignId);
var itemsExist = await query.AnyAsync();
int maxOrderIndex = 0;
if(itemsExist)
{
maxOrderIndex = await query.MaxAsync(x => x.OrderIndex);
}
在这里,您不必从数据库中检索所有项目,只需检查是否存在一条记录要快得多,并且还可以使该方法保持异步。
答案 2 :(得分:2)
先执行AnyAsync
然后执行MaxAsync
将导致两个单独的数据库调用。您可以通过确保序列包含“默认”最小值来将其压缩为一个。这是在您使用Linq Max / Min方法的任何地方的有用技巧,而不仅仅是在数据库代码中:
context.ExampleDbSet
.Where(w => w.ForeignId == foreignId)
.Select(s => s.OrderIndex)
.Concat(new[] { 0 })
.MaxAsync();
答案 3 :(得分:1)
如果默认值为您要获得的默认值(如果没有结果),则其性能比MaxAsync
好一点。
var highestOrderIndex = await _context.ExampleDbSet
.Where(x => x.ForeignId == foreignId)
.OrderByDescending(x => x.OrderIndex)
.Select(x => x.OrderIndex)
.FirstOrDefaultAsync();
TOP
比聚合函数要快,请查看SQL Server中的执行计划。
答案 4 :(得分:0)
您可以在 MaxAsync 之前使用 DefaultIfEmpty 和 Select。
var highestOrderIndex = await _context
.ExampleDbSet
.Where(x =>
x.ForeignId == foreignId)
.Select(x => x.OrderIndex)
.DefaultIfEmpty() // default is 0 if OrderIndex is int or long
// .DefaultIfEmpty(-1) // default is -1
.MaxAsync();