Row not found or changed. Linq 找不到行或行已更改
Linq to sql 中一个常见的问题:更新冲突。
这个词说起来比较玄乎,其实再平常不过了。通过一个简单的例子,来重现这个异常。
测试代码如下:
namespace LinqTest{
class Program{
static void Main(string[] args){
TestDataContext db = new TestDataContext();
db.Log = Console.Out;
var result = from p in db.LinqTests
where p.ID == 1
select p;
var info = result.FirstOrDefault();
if(info != null) { //插入断点
info.Age = 25;
db.SubmitChanges();
}
Console.ReadLine();
}
}
}
在正常情况下,是不会有错误的。
再进行第二次调试,首先,恢复Age的数据到以前的样子。下面我们运行到断点处,然后偷偷去SQL Server Management Studio中,手动修改数据,将原始数据中的Age,由24,改为22。然后回到VS2008的IDE,按F5继续运行程序,这个时候,你会发现异常出 现了。
查看执行的SQL语句和刚才的一样。这就是问题的所在,在正常运行状态下,Linq在运行时,会把数据库的数据缓存到实体对象中,这是一种理想化的情况, 并且在更新时,Linq会默认把除更新字段外的所有字段,作为 Update语句中的Where条件。但是,如果此时有另外的程序,在访问数据库,并修改数据库数据的时候,比如刚才把Age改为22。此时Linq缓存 起来的数据和实际数据库中的数据产生了不一致的情况。Linq此时仍然把被修改过的字段,作为Update的Where条件,但是数据库中Age早就被我 们改过了,不再是25,Where条件始终匹配不到原有的数据。这时,就会抛出所谓 的:“System.Data.Linq.ChangeConflictException: Row not found or changed.”异常。
产生此异常,主要是Linq缓存数据和实际数据库数据不一致的情况造成。解决次问题的情况,主要有几种:
1.比较简单的方法,不使用Linq提供的SubmitChanges()方式提交更改,而直接执行SQL语句,例如:
db.ExecuteCommand("Update [dbo].[LinqTest] SET Age=25 Where ID = @p0", 1);
这样虽然比较方便,但是感觉又回到了直接写SQL的时代,毕竟Linq to SQL的目的,就是为了让我们看不见SQL,避免写复杂的SQL语句,而直接操作实体对象,这样也可以避免程序可读性差、不便于维护。所以除非万不得已,还是不太推荐使用此方法。
2.参考MSDN的资料,采用Linq提供的解决更新冲突的方法,在异常中捕获冲突,然后手动解决冲突:
程序代码
try{
db.SubmitChanges(System.Data.Linq.ConflictMode.ContinueOnConflict);
}
catch (System.Data.Linq.ChangeConflictException ex){
foreach (System.Data.Linq.ObjectChangeConflict occ in db.ChangeConflicts)
{
//以下是解决冲突的三种方法,选一种即可
// 使用当前数据库中的值,覆盖Linq缓存中实体对象的值
occ.Resolve(System.Data.Linq.RefreshMode.OverwriteCurrentValues);
// 使用Linq缓存中实体对象的值,覆盖当前数据库中的值
occ.Resolve(System.Data.Linq.RefreshMode.KeepCurrentValues);
// 只更新实体对象中改变的字段的值,其他的保留不变
occ.Resolve(System.Data.Linq.RefreshMode.KeepChanges);
}
// 这个地方要注意,Catch方法中,我们前面只是指明了怎样来解决冲突,这个地方 还需要再次提交更新,这样的话,值 //才会提交到数据库。
db.SubmitChanges();
}
3. 这个方法也比较简单,也即MSDN中所说的Pessimistic Concurrency Control 。 我们可以来设定哪些字段需要放入Where条件,哪些字段不需要,这样就可以控制更新时候的条件匹配尺度。具体做法,就是在Linq to SQL Designer中,把一些字段的UpdateCheck属性设置为Never,这样,这些字段在更新的时候,就不会再出现在Where条件中了。其实比 较推荐的做法,就是在表中设立主键,因为更新的时候,只要把主键作为Where条件,就可以单独的确立一行数据了。
把除主键外的字段属性中 UpdateCheck设置为Never即可(UpdateCheck=UpdateCheck.Never)。

浙公网安备 33010602011771号