最近在程序运行中,发生一个比较怪异的问题导致程序异常退出(崩溃),开始没注意,以为是偶发问题。后来变成了必再现问题。查看Windows事件日志异常如下:
Application: XXXXX.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.Data.Linq.ChangeConflictException
Stack:
at System.Data.Linq.DataContext.SubmitChanges(System.Data.Linq.ConflictMode)
at NMS.PPC.DataSource.Database.DBOperator.SubmitChanges()
开始也没在意。觉得这个没什么,就是数据库更新时异常了。加个try-catch就解决了。结果发现另一个功能也发生了这个问题。由于Windows日志中没有抛出具体的Message,使用WinDebug分析Dump文件,看到了具体的消息,如下:
Exception object: 0000000010f87f48
Exception type: System.Data.Linq.ChangeConflictException
Message: 找不到行或行已更改。
InnerException: <none>
要更新的行明明存在怎么能找不到。我没更改呢,所以已更改不太可能发生。
因为研发环境无法再现,就将测试环境中的数据库和数据拿回到研发环境中,很快就再现了。确定问题确实存在。通过Linq输出SQL语句,发生所有的字段都在Where条件中出现了(这可能是Linq慢的原因吧)。
然后百度找到https://stackoverflow.com/questions/11189688/linq-to-sql-updating-without-refresh-when-updatecheck-never,说在Linq设计器将表列设置上UpdateCheck = Never。我是直接编码的,不用设计器。通过在DEMO程序中添加一个Linq设计,发现些设置在代码中就是在Column属性中加一个UpdateCheck即可。
即
[Column(UpdateCheck = UpdateCheck.Never)]
public double? AField{get;set;}
然后分析为什么旧的数据为什么出现,而新数据不出现。在分析崩溃原因时先不进行UpdateCheck = Never的设置。发现所有可空字段没设置值在Where条件中都是Is NULL判断。但AField却是=判断,但数据库中却没有值(将实例类字段值全输出发现AFiled居然有值)。
这个问题原因就找到了,确实按照Where条件查询不到记录了,异常也发生了。
然后到Linq实体类中去找代码,如下(代码中相关属性设置没添加):
private double? _AField;
public double? AField{
get{
if (_AFiled == null) return BField;
return _AFiled;
}
}
终于找到了原因,当AFiled是NULL时,直接取BField的值了,程序中确实对BField赋值了。
然后发现出问题的数据都是在此代码提交之前产生的数据,提交之后产生的数据正常。
总结
Linq2SQL中,如果是计算列,不用持久化到数据库中。所以可以不用加Column这个Attribute。
Linq2SQL中,Update时默认会把所有的持久化字段(即添加了Column这个Attribute的字段)作为Where条件,如果是在发布后的程序中添加新的非主键字段,建议在Column中添加上UpdateCheck = UpdateCheck.Never,这样在发布后更新旧的数据不会发生这种莫名的异常。