C# 从序列号池中将序列号分配给客户端

C# 从序列号池中将序列号分配给客户端,c#,asp.net,design-patterns,locking,license-key,C#,Asp.net,Design Patterns,Locking,License Key,我有一个sql server许可证密钥/序列号表。 表结构类似于 [ RecordId int, LicenceKey string, Status int (available, locked, used, expired etc.) AssignedTo int (customerId) .... ] 通过我的ASP.NET应用程序,当用户决定购买许可证时,单击“接受”按钮,我需要为用户保留许可证密钥。 我的做法是,, 从状态为可用的密钥表中选择前1个License密钥 更新密钥表设置状态

我有一个sql server许可证密钥/序列号表。 表结构类似于

[
RecordId int,
LicenceKey string,
Status int (available, locked, used, expired etc.)
AssignedTo int (customerId)
....
]
通过我的ASP.NET应用程序,当用户决定购买许可证时,单击“接受”按钮,我需要为用户保留许可证密钥。 我的做法是,, 从状态为可用的密钥表中选择前1个License密钥 更新密钥表设置状态=已锁定 然后将密钥返回到应用程序

我关心的是,如果两个asp.net线程访问相同的记录并返回相同的LicenseKey。 你认为做这些作业的最佳实践是什么?这类问题是否有一个众所周知的方法或模式? 如果需要,在哪里使用lock()语句

我正在使用SQLServer2005、数据访问存储过程、数据层、业务层和Asp.NETGUI


谢谢

我认为您实际上应该在查询密钥的同一存储过程中将该密钥标记为不可用,因为否则将始终存在某种竞争条件。手动锁定表不是一个好的做法

如果您有两个阶段的流程(例如预订机票),您可以引入在指定时间段(例如30分钟)内保留密钥的概念,以便在查询新密钥时,您可以同时保留密钥


编辑:如果您可以保证只有一个进程将更改数据库,那么锁定业务逻辑可能会起作用,但最好是在数据库级别执行,最好是在单个存储过程中。要正确执行此操作,您必须设置事务级别并在数据库中使用事务,正如@Adam Robinson在其回答中所建议的那样。

我认为您实际上应该在查询密钥的同一存储过程中将该密钥标记为不可用,否则将始终存在某种竞争条件。手动锁定表不是一个好的做法

如果您有两个阶段的流程(例如预订机票),您可以引入在指定时间段(例如30分钟)内保留密钥的概念,以便在查询新密钥时,您可以同时保留密钥


编辑:如果您可以保证只有一个进程将更改数据库,那么锁定业务逻辑可能会起作用,但最好是在数据库级别执行,最好是在单个存储过程中。要正确执行此操作,您必须设置事务级别并在数据库中使用事务,正如@Adam Robinson在其回答中所建议的那样。

要实现您所说的,您需要使用可序列化的事务。要执行此操作,请遵循以下模式:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
GO
BEGIN TRANSACTION

--Execute select
--Execute update

COMMIT TRANSACTION

但是,为什么有一个包含所有可能的许可证密钥的表?为什么不使用密钥生成算法,然后在用户购买密钥时创建一个新密钥?

要实现您所说的,您需要使用可序列化事务。要执行此操作,请遵循以下模式:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
GO
BEGIN TRANSACTION

--Execute select
--Execute update

COMMIT TRANSACTION

但是,为什么有一个包含所有可能的许可证密钥的表?为什么不使用密钥生成算法,然后在用户购买密钥时创建一个新密钥?

除了事务之外,您还可以尝试使用锁(在SQL中),以验证一次只有一个线程具有访问权限


我相信应用程序锁在这里可能会有所帮助。

除了事务之外,您还可以尝试使用锁(在SQL中),以验证一次只有一个线程具有访问权限


我相信应用程序锁在这里可能会有所帮助。

在这种情况下,可能不需要使用显式锁或事务

在存储过程中,您可以使用语句中的子句在单个原子操作中更新表并检索许可证密钥

大概是这样的:

UPDATE TOP (1) KeysTable
SET Status = 'locked'
OUTPUT INSERTED.LicenseKey
-- if you want more than one column...
-- OUTPUT INSERTED.RecordID, INSERTED.LicenseKey
-- if you want all columns...
-- OUTPUT INSERTED.*
WHERE Status = 'available'

在这种情况下,可能不需要使用显式锁或事务

在存储过程中,您可以使用语句中的子句在单个原子操作中更新表并检索许可证密钥

大概是这样的:

UPDATE TOP (1) KeysTable
SET Status = 'locked'
OUTPUT INSERTED.LicenseKey
-- if you want more than one column...
-- OUTPUT INSERTED.RecordID, INSERTED.LicenseKey
-- if you want all columns...
-- OUTPUT INSERTED.*
WHERE Status = 'available'

执照钥匙不属于我们。我们正在转售钥匙。许可证钥匙不属于我们。我们正在转售密钥。这根本不能解决他的问题…他在同一批中执行选择和更新,但因为他没有任何类型的锁(如可序列化事务)无法保证并行批处理不会选择同一行。实际上,我计划在业务层编写一个静态方法,如GetNextAvailableKey,该方法在数据访问方法(如select key)周围有一个lock()块,更新密钥等。或者我应该编写一个具有可序列化隔离级别的GetNextKey存储过程,用于选择、更新和返回密钥,并在调用方法中添加lock()块?抱歉,我的回答有点不准确。请查看编辑。这根本不能解决他的问题…他在同一批中执行选择和更新,但因为他没有任何类型的锁(如可序列化事务)无法保证并行批处理不会选择同一行。实际上,我计划在业务层编写一个静态方法,如GetNextAvailableKey,该方法在数据访问方法(如select key)周围有一个lock()块,更新密钥等。或者我应该编写一个具有可序列化隔离级别的GetNextKey存储过程,用于选择、更新和返回密钥,并在调用方法中添加lock()块?抱歉,我的回答有点不准确。请查看编辑。如果我想返回整个记录而不是仅返回LicenseKey字段,该怎么办?我可以这样使用:Update TOP(1)KeysTable SET Status=“locked”从KeysTable中选择*,其中licenceKeyId=INSERTED.licenceKeyId谢谢。不,您不能这样做。我会更新我的答案来展示它是如何完成的。很好,但不幸的是我在这张桌子上有一个触发器。所以它不允许我在没有输入的情况下使用输出。如果我能找到我们