最近在开发一个小型的物业管理系统,系统其中有一个功能需要每个月按抄的水表、电表等生成相应的费用,数据库主要的基础数据表有大楼水、电表、楼层水、电表、房间水电表;其中大楼和楼层的水电表是用于计算公摊的;系统设计有一个费用的统计表,表名ChargeAccountMaster,表内设计的有一个字段ID,主键 、 自增长;计算时由于是数据核算统计,所以引入事务计算数据的同时,也会把相应计算的结果回写回基础数据表中,计算的类是service层,框架的ORM用的是EF,就没有采用本地事务,采用了分布式事务TransactionScope,代码如下:
TransactionOptions transactionOption = new TransactionOptions(); //设置事务隔离级别 transactionOption.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted; // 设置事务超时时间为120秒 transactionOption.Timeout = new TimeSpan(6000000000); using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, transactionOption)) { .....................业务代码 scope.Complete(); }
最初数据统计写入数据库的场景是,先初始化ChargeAccountMaster 表,对表内的每一个字段的值都进行初始化,插入数据库;然后在计算和统计每一个基础数据表后得出结果再Update该表,在开发的时候,这种方式一直没有出错,直接发布到生产环境,将正式的数据导过来,进行大查的数据核算时结果报错了:存储区更新、插入或删除语句影响到了意外的行数(0)。实体在加载后可能被修改或删除。刷新 ObjectStateManager 项 ;刚见到这个错误时,头都大了,没有任何的提醒,只好找度娘,找到如下答案:
主键没有赋值,造成数据库的值与程序的主键值的ID没有对应上
通过调试,由于数据库的ID设计为自增,所以在程序时中就没有直接赋值,跟踪到数据插入,发觉自增的ID在EF 第一次 SaveChange()后,是有返回ID的,且插入数据是成功的,但由于数据的错误,事务却未能提交;但第二就插入不成功,就开始报这个错误了。
一直以为大家都写的ID没值,刚好第二次确实没有返回值,一直以为就是ID的问题,就在这上面花了很多时间,调整事务的隔离属性、范围内、把自增的ID改为程序生成写入都没有效果,还是报同样的错误;最后没办法,在想第一交都有值,那么只做一次性插入,不做前面的初始化,初始化的动作在程序上做,每计算完一个基础数据然后直接在程序上更新,最后统一插入和更新数据库,结果却成功了,问题解决了,却找不到出现问题的原因。
个人分析:事务是不是在插入数据后(SaveChange),不允许再对当前的数据进行更新,那么为什么在开发的时候,却没有了出现这个问题?
将问题记录在这,找到问题的原因再来更。。。。。。