faster/better 选项是在 EF 中获取最后一个可能的 Id 的选项:MaxAsync 与 LastAsync?
Which one is faster/better option to obtain last possible Id in EF: MaxAsync vs LastAsync?
我有一个 table 在 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);
但后来我想知道 MaxAsync
与 LastAsync
有什么不同。
来自 Microsoft documentation 我看到 MaxAsync
Asynchronously returns the maximum value of a sequence.
与此同时 LastAsync
Microsoft documentation
Asynchronously returns the last element of a sequence that satisfies a specified condition.
有什么区别,为了获得最后可能的 Id,最好使用这两者中的哪一个。
区别在于MaxAsync
returns是序列中最大的值,而LastAsync
returns是final 序列中的值,如您在问题中所述。如果你的table的ID是保证是升序的,每次递增1,那么这两个的结果总是一样的,虽然我怀疑LastAsync
会更快,因为它需要做的就是找到集合的长度并访问该索引处的值,而 MaxAsync
必须遍历集合以找到其中的最大值。
至于什么是更好的解决方案,这取决于您对 ID 在数据库的整个生命周期中一直递增的确定性。如果你知道每条新记录它们总是递增 1,我认为使用 LastAsync
没有问题。如果您不这样做,MaxAsync
将确保您始终可以生成一个大于之前最大的唯一 ID 1,但要花费一些 运行 时间。
“更快”你必须通过测量来确定,因为有很多因素可以影响它。也许作为您研究的一部分,您可以分析目标数据库并观察 EF 发送给它的查询并查看您喜欢哪个查询。
“更好”在这里是一个有点不同的问题。对于初学者来说,需要注意的一个主要区别是 MaxAsync
将始终 return 该列的最大值,但 LastAsync
没有这样的保证 。它将 return 结果中的最后一条记录,但最后一条记录是什么?它可能 将在您的设置中拥有最高的 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 和存储新记录之间的时间里,另一个用户是否会执行相同的操作?这不太可能,但不能保证。基本上它是永远不会发生的情况之一......直到它发生。
理想的解决方案是更新数据库结构以使用它自己的 internally-generated ID(auto-increment,身份,不同的 RDBMS 有不同的术语)。如果出于某种原因这不是一个选项,那么更新数据库结构以使用 GUID 是 client-generated ID 的常见可行选项。
如果其中任何一个都失败,您会发现自己处于潜在的竞争条件情况下,并且您希望尽可能缩小 window 的范围。也许通过创建一个执行此操作的存储过程,所以客户端系统仍然只调用一个数据库操作,并且在该存储过程中你会有一个 INSERT
和一个 SELECT
的嵌套 fetches/increments身份证。不理想,但可以工作。
我不建议您使用这些函数中的任何一个来获取新的 ID。如果您的数据库中有一个(且只有一个)用户,则可以使用它们(我认为 LastAsync
会更快)。但是,如果两个用户同时添加新记录会怎样?他们将获得相同的id值!
因此考虑使用 db built-in 功能来生成新的 ID。您应该检查数据库文档它是什么 - 例如,MS SQL 使用序列,Firebird - 生成器等。您可以使用 BEFORE INSERT
触发器在 db-side.
上获取新的 id 值
我有一个 table 在 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);
但后来我想知道 MaxAsync
与 LastAsync
有什么不同。
来自 Microsoft documentation 我看到 MaxAsync
Asynchronously returns the maximum value of a sequence.
与此同时 LastAsync
Microsoft documentation
Asynchronously returns the last element of a sequence that satisfies a specified condition.
有什么区别,为了获得最后可能的 Id,最好使用这两者中的哪一个。
区别在于MaxAsync
returns是序列中最大的值,而LastAsync
returns是final 序列中的值,如您在问题中所述。如果你的table的ID是保证是升序的,每次递增1,那么这两个的结果总是一样的,虽然我怀疑LastAsync
会更快,因为它需要做的就是找到集合的长度并访问该索引处的值,而 MaxAsync
必须遍历集合以找到其中的最大值。
至于什么是更好的解决方案,这取决于您对 ID 在数据库的整个生命周期中一直递增的确定性。如果你知道每条新记录它们总是递增 1,我认为使用 LastAsync
没有问题。如果您不这样做,MaxAsync
将确保您始终可以生成一个大于之前最大的唯一 ID 1,但要花费一些 运行 时间。
“更快”你必须通过测量来确定,因为有很多因素可以影响它。也许作为您研究的一部分,您可以分析目标数据库并观察 EF 发送给它的查询并查看您喜欢哪个查询。
“更好”在这里是一个有点不同的问题。对于初学者来说,需要注意的一个主要区别是 MaxAsync
将始终 return 该列的最大值,但 LastAsync
没有这样的保证 。它将 return 结果中的最后一条记录,但最后一条记录是什么?它可能 将在您的设置中拥有最高的 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 和存储新记录之间的时间里,另一个用户是否会执行相同的操作?这不太可能,但不能保证。基本上它是永远不会发生的情况之一......直到它发生。
理想的解决方案是更新数据库结构以使用它自己的 internally-generated ID(auto-increment,身份,不同的 RDBMS 有不同的术语)。如果出于某种原因这不是一个选项,那么更新数据库结构以使用 GUID 是 client-generated ID 的常见可行选项。
如果其中任何一个都失败,您会发现自己处于潜在的竞争条件情况下,并且您希望尽可能缩小 window 的范围。也许通过创建一个执行此操作的存储过程,所以客户端系统仍然只调用一个数据库操作,并且在该存储过程中你会有一个 INSERT
和一个 SELECT
的嵌套 fetches/increments身份证。不理想,但可以工作。
我不建议您使用这些函数中的任何一个来获取新的 ID。如果您的数据库中有一个(且只有一个)用户,则可以使用它们(我认为 LastAsync
会更快)。但是,如果两个用户同时添加新记录会怎样?他们将获得相同的id值!
因此考虑使用 db built-in 功能来生成新的 ID。您应该检查数据库文档它是什么 - 例如,MS SQL 使用序列,Firebird - 生成器等。您可以使用 BEFORE INSERT
触发器在 db-side.