本文接着上篇Linq to SQL之查询和添加,还是以Northwind数据库为例,介绍使用Linq to SQL怎样对数据库的数据进行更新及冲突的相关问题。
首先对Customers表的一条记录进行更新:
NorthwindDataContext ctx = new NorthwindDataContext(); Customer alfki = ctx.Customers.Single(c => c.CustomerID == "ALFKI"); Console.WriteLine("Before update, the company name of Alfki is: " + alfki.CompanyName); alfki.CompanyName = "New Company Name"; ctx.SubmitChanges(); Customer newAlfki = ctx.Customers.Single(c => c.CustomerID == "ALFKI"); Console.WriteLine("After update, the company name of Alfki is: " + alfki.CompanyName);
通过屏幕输出,可以看到数据的确已经更新成功了
Before update, the company name of Alfki is: Alfreds Futterkiste
After update, the company name of Alfki is: New Company Name
现在,把调用SubmitChanges方法放在最后,看看会发生什么:
NorthwindDataContext ctx = new NorthwindDataContext(); Customer alfki = ctx.Customers.Single(c => c.CustomerID == "ALFKI"); Console.WriteLine("Before update, the company name of Alfki is: " + alfki.CompanyName); alfki.CompanyName = "New Company Name"; Customer newAlfki = ctx.Customers.Single(c => c.CustomerID == "ALFKI"); Console.WriteLine("Before submit changes, the company name of Alfki is: " + alfki.CompanyName); ctx.SubmitChanges();
屏幕的输出为:
Before update, the company name of Alfki is: Alfreds Futterkiste
Before submit changes, the company name of Alfki is: New Company Name
可以看到,第二次查询是在更新提交之前,这时候数据库中的值还没有改变,按理说这时候去进行数据库查询得到的值应该还是旧值,但是代码运行后发现查询出来的值确是更新后的值。这是为什么呢?其实原因还是上篇提到过的Identity Cache,DataContext以主键为key对数据对象进行缓存以此对数据对象的生命周期进行跟踪。借助Sql profile可以发现,第二次调用ctx.Customers.Single时,数据库并没有查询的记录,这里DataContext直接在内存中查找该对象并且返回。当然也可以关闭这种机制,只需要调用ctx.ObjectTrackingEnabled = false。DataContext就不再对数据对象进行Identity Cache,每次查询结果都是通过查询数据库得到。
数据库数据更新通常会遇到同步冲突的问题,比如获得数据以后,对数据进行一系列的操作,然后把新的数据更新回数据库。如果在数据进行操作的同时,有其它程序或者管理员改动了该数据的值,这样就会发生冲突。到底数据是应该保留现有的值呢还是把改动的值强行更新到数据库呢?Linq to SQL提供了很多方法来解决这种冲突问题。
还是一段简单的更新代码:
NorthwindDataContext ctx = new NorthwindDataContext(); Customer alfki = ctx.Customers.Single(c => c.CustomerID == "ALFKI"); alfki.CompanyName = "New Company Name"; ctx.SubmitChanges();
在SubmitChanges()加上断点然后运行,接着在数据库管理器中修改这条记录的CompanyName,最后回到vs界面,继续往下运行,这时会抛出ChangeConflictException,提示该行记录已经被删除或者被修改
在Linq to SQL中,解决冲突有几种方法:
- 在映射字段的Column Attribute中添加UpdateCheck属性
[Column(Storage="_CompanyName", DbType="NVarChar(40) NOT NULL", CanBeNull=false, UpdateCheck=UpdateCheck.Never)] public string CompanyName {
UpdateCheck的意思是在更新的时候是否检查冲突,分为三种,根据自己的需求进行选择:
- Always 始终检查
- Never 从不检查
- WhenChanged 当数据有改动的时候检查
2. 使用ObjectChangeConflict的Resolve方法,比如:
try { ctx.SubmitChanges(); } catch (ChangeConflictException) { foreach (ObjectChangeConflict confict in ctx.ChangeConflicts) { confict.Resolve(RefreshMode.KeepCurrentValues); } } finally { ctx.SubmitChanges(); }
这里RefreshMode就是表示解决冲突的方法,也有三种:
- KeepChanges 把改变过的属性值更新到数据库,没有改变过的属性值就用数据库的当前值
- KeepCurrentValues 把当前所有值更新到数据库
- OverwriteCurrentValues 使用数据库的当前值,不做强行更新