首先,我认为您在问题中描述的方式最好的办法用于以 MS SQL 作为数据库的 ASP.NET 应用程序。数据库中没有锁定。与它完美搭配永久断开连接的客户端就像网络客户端一样。
如何从一些答案中解读,术语中存在误解。我们都指使用 Microsoft SQL Server 2008 或更高版本来保存数据库。如果您在 MS SQL Server 2008 文档中打开主题“rowversion (Transact-SQL)”,您将发现以下内容:
"时间戳是的同义词行版本数据类型并受
数据类型同义词的行为。”...
“这时间戳语法已弃用。
此功能将在
Microsoft SQL 的未来版本
服务器。避免在以下情况中使用此功能
新的开发工作,并计划
修改当前使用的应用程序
此功能。”
So 时间戳数据类型是同义词行版本MS SQL 的数据类型。它保存 64 位计数器,该计数器存在于每个数据库内部,可以看作@@DBTS。在数据库的一张表中修改一行后,计数器将增加。
当我读到你的问题时,我读到“TimeStamp”作为该类型的列名行版本数据。我个人更喜欢这个名字行更新时间戳。在 AzManDB(请参阅 Microsoft Authorization Manager with the Store as DB)中,我可以看到这样的名称。有时也被使用子更新时间戳追踪层次结构行更新时间戳结构(关于触发器)。
我在上一个项目中实现了这种方法,并且非常高兴。一般来说,您会执行以下操作:
- Add 行更新时间戳p 列到数据库中每个表的类型行版本(它将在 Microsoft SQL Management Studio 中显示为时间戳,这是相同的)。
- 您应该构建所有 SQL SELECT 查询以将结果发送到客户端,以便您发送额外的结果行版本值与主要数据一起。如果你有一个带 JOINT 的 SELECT,你应该发送行版本最大的行更新时间戳两个表中的值,例如
SELECT s.Id AS Id
,s.Name AS SoftwareName
,m.Name AS ManufacturerName
,CASE WHEN s.RowUpdateTimeStamp > m.RowUpdateTimeStamp
THEN s.RowUpdateTimeStamp
ELSE m.RowUpdateTimeStamp
END AS RowUpdateTimeStamp
FROM dbo.Software AS s
INNER JOIN dbo.Manufacturer AS m ON s.Manufacturer_Id=m.Id
或者进行如下数据转换
SELECT s.Id AS Id
,s.Name AS SoftwareName
,m.Name AS ManufacturerName
,CASE WHEN s.RowUpdateTimeStamp > m.RowUpdateTimeStamp
THEN CAST(s.RowUpdateTimeStamp AS bigint)
ELSE CAST(m.RowUpdateTimeStamp AS bigint)
END AS RowUpdateTimeStamp
FROM dbo.Software AS s
INNER JOIN dbo.Manufacturer AS m ON s.Manufacturer_Id=m.Id
to hold 行更新时间戳 as bigint,对应于ulongC# 的数据类型。如果您从许多表创建 OUTER JOINT 或 JOINT,则构造MAX(RowUpdateTimeStamp)
从所有表中都会看到有点复杂。由于 MS SQL 不支持 MAX(a,b,c,d,e) 等函数,因此相应的构造可能如下所示:
(SELECT MAX(rv)
FROM (SELECT table1.RowUpdateTimeStamp AS rv
UNION ALL SELECT table2.RowUpdateTimeStamp
UNION ALL SELECT table3.RowUpdateTimeStamp
UNION ALL SELECT table4.RowUpdateTimeStamp
UNION ALL SELECT table5.RowUpdateTimeStamp) AS maxrv) AS RowUpdateTimeStamp
- 所有断开连接的客户端(Web 客户端)不仅接收并保存某些数据行,而且行版本 (type ulong) 的数据行。
- 在一次尝试修改来自断开连接的客户端的数据时,您的客户端应该发送行版本对应于服务器的原始数据。这
spSoftwareUpdate
存储过程可能看起来像
CREATE PROCEDURE dbo.spSoftwareUpdate
@Id int,
@SoftwareName varchar(100),
@originalRowUpdateTimeStamp bigint, -- used for optimistic concurrency mechanism
@NewRowUpdateTimeStamp bigint OUTPUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
-- ExecuteNonQuery() returns -1, but it is not an error
-- one should test @NewRowUpdateTimeStamp for DBNull
SET NOCOUNT ON;
UPDATE dbo.Software
SET Name = @SoftwareName
WHERE Id = @Id AND RowUpdateTimeStamp <= @originalRowUpdateTimeStamp
SET @NewRowUpdateTimeStamp = (SELECT RowUpdateTimeStamp
FROM dbo.Software
WHERE (@@ROWCOUNT > 0) AND (Id = @Id));
END
Code of dbo.spSoftwareDelete
存储过程看起来是一样的。如果你不开机NOCOUNT
,你可以产生DB并发异常在很多场景中自动生成。 Visual Studio 为您提供了使用乐观并发的可能性,例如高级选项中的“使用乐观并发”复选框TableAdapter
or DataAdapter
.
如果你看dbo.spSoftwareUpdate
细心的你会发现,我使用的存储过程RowUpdateTimeStamp <= @originalRowUpdateTimeStamp
在 WHERE 而不是RowUpdateTimeStamp = @originalRowUpdateTimeStamp
。我这样做是因为,@originalRowUpdateTimeStamp
其客户端通常被构造为MAX(RowUpdateTimeStamp)
来自多个表格。所以可能是这样RowUpdateTimeStamp < @originalRowUpdateTimeStamp
。要么你应该使用严格平等=并在此处重现与 SELECT 语句中使用的相同的复杂 JOIN 语句或使用<=像我一样构建并保持与以前一样的安全。
顺便说一句,人们可以为ETag基于 RowUpdateTimeStamp,它可以在 HTTP 标头中与数据一起发送到客户端。随着ETag您可以在客户端实现智能数据缓存。
我无法在这里编写完整的代码,但是你可以在互联网上找到很多示例。我只想再重复一次,在我看来,使用基于乐观并发的方法行版本 is 适用于大多数 ASP.NET 场景的最佳方法.