在《在Linq to Sql中管理并发更新时的冲突(2):引发更新冲突》一文中 ,我们描述了Linq to Sql检测在更新时是否产生了冲突的基本方法:将该记录 每个字段原来的值和更新时的值进行对比,如果稍有不同则意味着记录被修改过 ,因此产生了更新冲突。不过您是否有这样的感觉,这种方法实在累赘了一些? 如果一个表中有数十个字段,那么更新就必须完整地检测一遍(不过我会在今后 的文章中提到这方面的控制)。再者,如果其中某一个字段储存了洋洋洒洒上万 字的文章,那么在验证时仅仅是将它从Web服务器发送到数据库服务器就需要耗 费可观的带宽与时间,这是不是显得有些“得不偿失”呢?
因此Linq to Sql提供了另外一种检测并发更新冲突的方式:使用记录的时间戳 。这并不是Linq to Sql特有的功能,如果您了解其他的ORM框架的话,就会发现 诸如Hibernate也提供了类似的机制——自然,在使用上不会像Linq to Sql那样方便。
在Sql Server中设计数据表时,我们可以使用一个特 殊的数据类型:timestamp。请不要将它与SQL-2003标准中的timestamp类型混淆 起来,那里的timestamp和Sql Server中的datetime比较相似(Oracle中 timestamp的概念符合SQL-2003标准,而MySql中timestamp的概念与Sql Server 相同),而Sql Server中的timestamp与SQL-2003标准中的rowversion类型对应 。Sql Server中的timestamp类型和binary(8)在存储上非常类似(不过nullable 的timestamp和nvarchar(8)类似),从类型名称上我们就可以看出,这是一个 “时间戳”字段:当数据表中的某一条记录被添加或者修改之后, Sql Server会自动向类型为timestamp的字段写入当前时间。换句话说,只要在 更新时发现该字段的值没有被修改过,就表明没有产生并发冲突。
我们 还是通过一个例子来体验一下吧。
如上图。我们定义了一个新的数据表,其中有个 record_version字段为timestamp类型,这就是记录的时间戳(record_version 这个字段名似乎有点不太“雅观”,我觉得我们不会去主动使用它, 所以问题不大——当然一些静态检查工具可不这么认为:))。有了记 录的时间戳,我们就可以在检测更新冲突时获得更好的性能了。
try
{
LinqToSqlDemoDataContext dataContext = new LinqToSqlDemoDataContext();
Order order = dataContext.Orders.Single(o => o.OrderID == 1);
order.Name = "New Order Name";
dataContext.Log = Console.Out;
// 在下面的语句上设置一个断点
dataContext.SubmitChanges();
}
catch (ChangeConflictException e)
{
Console.WriteLine (e.Message);
}
Console.ReadLine();