C# 在EF:MaxAsync和LastAsync中,哪一个更快/更好地获得最后一个可能的Id?

C# 在EF:MaxAsync和LastAsync中,哪一个更快/更好地获得最后一个可能的Id?,c#,entity-framework,linq,ef-core-3.1,C#,Entity Framework,Linq,Ef Core 3.1,我有一个在Id列上没有自动递增的表。在创建记录时,我需要设置Id,但问题出现了,我如何为新记录获取可能的Id 我的第一次尝试是: var ids = await db.Table.Select(e => e.Id).ToListAsync(); var id = Enumerable.Range(1, int.MaxValue).Except(ids).First(); 这似乎太重了,应该有更好的办法。 第二次尝试: var id = await db.Table.MaxAsync(e

我有一个在Id列上没有自动递增的表。在创建记录时,我需要设置Id,但问题出现了,我如何为新记录获取可能的Id

我的第一次尝试是:

var ids = await db.Table.Select(e => e.Id).ToListAsync();
var id = Enumerable.Range(1, int.MaxValue).Except(ids).First();
这似乎太重了,应该有更好的办法。 第二次尝试:

var id = await db.Table.MaxAsync(e => e.Id);
但是我想知道
maxancy
lastancy
有什么不同

从中我看到
maxancy

异步返回序列的最大值

同时,这是
LastAsync

异步返回满足指定条件的序列的最后一个元素


区别是什么?为了获取最后一个可能的Id,这两个选项中的哪一个更好。

区别在于
maxancy
返回序列中的最大值,而
lastancy
返回序列中的最终值,如您在问题中所述。如果表的ID保证为升序,每次递增1,那么这两个ID的结果总是相同的,尽管我怀疑
LastAsync
会更快,因为它只需要找到集合的长度并访问该索引处的值,而
maxancy
必须遍历集合以查找其中的最大值

至于更好的解决方案是什么,这取决于您是否确定在数据库的整个生命周期中ID一直在增加。如果您知道,对于每一条新记录,它们总是递增1,那么使用
LastAsync
就没有问题。如果您不这样做,
maxancy
将确保您始终可以以一些运行时间为代价,生成一个比前一个最大ID大的唯一ID 1。

您必须通过测量来确定“更快”,因为有许多因素可能会影响到这一点。也许作为研究的一部分,您可以分析目标数据库,观察EF发送给它的查询,并查看您喜欢哪个查询

“更好”在这里是一个有些不同的问题。对于初学者来说,需要注意的一个主要区别是
maxancy
将始终返回该列的最大值,但是
lastancy
不提供这样的保证。它将返回结果中的最后一条记录,但最后一条记录是什么?它可能在您的设置中具有最高的ID,但这不能保证。因为如果不指定排序,则无法保证SQL结果的排序顺序。例如:

var id = await db.Table.OrderBy(e => e.Id).Select(e => e.Id).LastAsync();
或者,按降序排序并只获取第一条记录可能会“更快”:

var id = await db.Table.OrderByDescending(e => e.Id).Select(e => e.Id).FirstAsync();
(这是另一种情况,在这种情况下,“更快”对您来说很有意思。即使在操作链上加入一个
。Take(1)
,也可能会有所不同。)

但让“更好”更有趣的是,这个整体设置本质上是一个等待发生的竞争条件。是否会有多个用户同时使用此系统?在获取“最后一个”ID和存储新记录之间,另一个用户是否会执行相同的操作?这不太可能,但不能保证。基本上这是一种永远不会发生的情况。。。直到它发生

理想情况下,这里的解决方案是更新数据库结构,以使用自己内部生成的ID(自动增量、标识、不同的RDBMS有不同的术语)。如果出于任何原因这不是一个选项,那么更新数据库结构以使用GUID是客户端生成ID的常见可行选项


如果这两种情况都失败了,你会发现自己处于一种潜在的比赛状态,你希望尽可能缩小这个窗口。也许通过创建一个执行此操作的存储过程,客户机系统仍然只调用一个DB操作,在该存储过程中,您将有一个带有嵌套的
SELECT
INSERT
,它获取/增加ID。这不理想,但是可以工作。

我不建议您使用这些函数中的任何一个来获取新id。如果您的数据库中有一个(并且只有一个)用户,那么使用它们就可以了(我认为,
LastAsync
会更快)。但如果两个用户同时添加新记录,会发生什么?他们将获得相同的id值


<>考虑使用DB内置的特性来生成新的ID。您应该检查数据库文档“它是什么”——例如,MS SQL使用序列、Firebird生成器等等。您可以在插入前使用
触发器来获取数据库端的新id值。

如果您想生成一个最大值为+1的新id,两者都不是-这是一个非常糟糕的id选择,很容易导致重复或无效关系为什么不向该列添加标识约束?如果您不需要标识,可以使用,并将MySequence的列的默认值设置为
NEXT value
,至于性能,
Last()
需要一个结果集来处理,并且具有特定的顺序,这意味着它需要一个
order BY
。要获得最后一个值,它必须迭代整个结果集。这不能通过索引来加速。如果您颠倒顺序并选择第一项,速度会更快。另一方面,最小值和最大值是在服务器上计算的,可以利用索引。如果一列被索引覆盖,
MAX
是索引中的最后一个叶值。除非由数据库负责,否则绝对不能确定这些值是否一致递增。如果您的表的ID保证按升序排列,每次递增1,OP会询问错误或数据丢失的常见原因,而实际上并非如此。