.net的这经典bug

mooniscrazy 2003-08-24 05:38:57
最近在做项目时,发现.net对自动编号的表多条数据进行更新时有个严重bug,导致项目几乎要重做。
1、先建这样一个表
create table tb(id int identity primary key,
value char(10));
2、用.net ide的服务器资源管理器将这个表拖到窗体上,讲设计器生成的连接命名为con,数据适配器命名为sda,然后右击适配器,将生成的类型化数据集命名为ds
3、在窗体上添加添加一个按钮button1,按钮的代码如下:
try
{

ds1.Clear();
for (int i=0;i<30;i++)
{
ds.tbRow r=ds1.tb.NewtbRow();
r.BeginEdit();
r.value="aaa";
r.EndEdit();
ds1.tb.AddtbRow(r);
}
con.Open();
}
sda.Update(ds1.tb);
MessageBox.Show("ok");
}
catch(System.Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
con.Close();
}
4、运行代码,会产生这样的错误
列"id"被约束为唯一的,值1已存在
5、有时候这种现象会消失,但是如果新建一个表,一定会重复出现。而且最可怕的是,这种现象会不规则的出现。
6、以下sql代码可以重建这个表
drop table tb;
create table tb(id int identity primary key,
value char(10));
select count(*) from tb
7、如果自己用windows生成的sql命令,则可以更新数据,但是会引起删除时的并发错误问题。
try
{

ds1.Clear();
for (int i=0;i<30;i++)
{
ds.tbRow r=ds1.tb.NewtbRow();
r.BeginEdit();
r.value="aaa";
r.EndEdit();
ds1.tb.AddtbRow(r);
}
con.Open();
for(int i=0;i<ds1.tb.Count;i++)
{
sda.InsertCommand.Parameters["@value"].Value=ds1.tb[i].value;
sda.InsertCommand.ExecuteNonQuery();
} MessageBox.Show("ok");
}
catch(System.Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
con.Close();
}

大家有兴趣可以讨论一下,看有没有办法解决这个大型bug
...全文
143 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
mooniscrazy 2003-08-31
  • 打赏
  • 举报
回复
这个问题我知道原因了,确实如绿叶兄所说,微软在datatable更新时,没有考虑到identity字段会改变datatable的主键造成重复值的问题。一个权宜之记是更新时暂时禁用identity字段的unique属性。
浪子兄所说的办法,我正在做,不过要完整实现,代码量很大。而且,拼装sql语句,会隐藏一些错误,比如用户在输入时输入引号,会使数据库出错。甚至用户在输入时恶意输入一些sql语句,会看到本来不该看到的东西。我觉得好的办法是使用sqlcommand或者存储过程来传入输入参数。此外,.net访问数据是脱机访问方式,不锁定数据库,所以要考虑一个并发的问题,因为在你更新数据库某一条记录时,可能该数据被别人更新过,所以会使前一个用户的更新丢失。所以,要自己解决这个问题,代码量是很大的,只有借助于代码生成工具。我正在做这样一个东西。。。
jiezhi 2003-08-25
  • 打赏
  • 举报
回复
建議把數據操作(insert,update,delete)寫成自己的方法,讓用戶傳入值,你在方法里面拼裝成sql語句,然后送到數據庫執行之。這樣就不會有什么問題,而且效率也不錯。當然注意數據的操作基本都是在內存中,到提交時候才訪問數據庫。
colinmad 2003-08-24
  • 打赏
  • 举报
回复
up
layershow 2003-08-24
  • 打赏
  • 举报
回复
我看了自动生成的数据集.
好象不能自动取得开始位置和增量
所以数据库中没数据的时候就本地的id会变成从0开始.
楼主,是不是有数据时不会发生这种错误?
layershow 2003-08-24
  • 打赏
  • 举报
回复
原来如此,前面说的可能不是很对.
呵呵,都是个人理解.:)

我追踪一下每条记录的Insert过程
问题并不是出在Sql语句上.
问题出在DataTable上
确实是偶尔发生
这个问题是这样的:
因为在DataTable中定义了主键,就是id
在新建行的时候它没有被手工赋值.
于是自动产生一个数字.
正如我所说.DataSet是脱机操作.并不能知道数据源的目前id
可能会被赋值为如下序列:0,1,2,3,4
在更新第0条的时候.数据成功了,但是@@identity 是1 因为数据库中是从1 开始的
于是自动Update要更新第1条数据(id 为 0 这条)
DataTable的id被设为主键.不能重复.于是DataTable不能更新这单条记录
与第二条的id重复(这条的id还是1,如果它被Insert就会变成2,但是还没有,所以报错)
这个错误不是数据库中的错误
是DataTable本地错误.无法同时允许两个相同id(所有行都Update完不会有这种情况)
过程如下 (数据库从1 开始,这里是说的DataTable中的id)
0,1,2,3,4 初始状态
1,1,2,3,4 更新第一条报错 有两个1
1,2,2,3,4 如此循环下去......但是前面就抛出异常了

楼主所说的偶尔发生是这种情况
DataTable中的id是 100,101,102,103
数据库中是从1开始的.
过程就是:
1,101,102,103 更新第一条
1,2,102,103 更新第二条
1,2,3,103 更新第三条
1,2,3,4 更新第四条
这样更新到最后也不会出错

我也不能说这是不是Bug了.呵呵.但问题就是这样
解决办法就是把生的数据集中 对自增序列的PK去掉
允许在DataTable中某时刻有重复id(只是瞬间,呵呵)

一般自增序列是由数据库维护的.不能编辑,定义它为PK没有什么意思.因为必不会重复
建议PK就加在别的字段上面吧.

mooniscrazy 2003-08-24
  • 打赏
  • 举报
回复
绿叶兄,我不是说你,我发贴时才看到你的回贴,不好意思。。。
mooniscrazy 2003-08-24
  • 打赏
  • 举报
回复
不好意思,上面的代码里,我在con.open()的后面多了一个括号,拷贝时不小心带过来的,大家如果要试验的话,请把这个括号去掉。这段代码我是多次查证过的,微软的帮助里也是这样更新数据的,请大家不要再轻易怀疑我的代码,至少先试试再说。
layershow 2003-08-24
  • 打赏
  • 举报
回复
嗯。那可能是我想错了。
我试一试你的代码
mooniscrazy 2003-08-24
  • 打赏
  • 举报
回复
回复人: layershow(绿叶兄)
老兄,首先谢谢你的热情回复。
但是抱歉的是,你说的并不正确。因为datatable里面的序列号是无用的。你看看adapter里面生成的insertcommand就知道,里面的参数没有自动生成的主键。而且数据库在插入的时候,是没有并发冲突的问题的,数据库自己自动生成一个编号值,而适配器只是在插入之后,立刻返回这个编号值。而且,你对多个客户端同时运行的情况的理解,我也不敢苟同。你可以仔细研究一下vs生成的command,相信会有收获的。请看vs生成的insertcommand:
this.sqlInsertCommand1.CommandText = "INSERT INTO tb(value) VALUES (@value); SELECT id, value FROM tb WHERE (id = @@IDE" +
"NTITY)";
this.sqlInsertCommand1.Connection = this.con;
this.sqlInsertCommand1.Parameters.Add(new System.Data.SqlClient.SqlParameter("@value", System.Data.SqlDbType.VarChar, 10, "value"));

而且,我自己用代码手工调用这条insertcommand是可以完成自动编号的更新的,我上面的代码里面有。而且,搞成这样,我相信并不是microsoft的本意。还有,这段代码出错也不是必然的,而是偶然的,有时候出错,有时候没问题。运行环境是一样的。正因为这样,所以不敢用。其实,我对dataset是不喜欢的,因为它不符合面向对象的原则,数据和界面耦合太强,dataset会把编程者导向一个错误的方向,所以以后我不打算再用dataset了。那些访问数据的命令,完全可以自己实现。不但可以用于.net,还可以运用于其他编程语言。关键是要实现一个代码模板,否则编程量太大。微软不是万能的,而且有时候,它犯的错误也不少,而且是严重的错误。
layershow 2003-08-24
  • 打赏
  • 举报
回复
是这个问题呀.
这并不是Bug.当然你也可以这么认为
首先说一个你写的代码问题.在Update之前.不Fill是不太好的做法
数据服务器并不是你一个人在用(或者只有你一个人在用吧).
在你更改DataTable的时候,可能有其他人更新了数据库.
这是可能情况之一.为了与其他人协作.
考虑一下吧.这样你只把自己的数据更新到数据库
意思就是在你Update的时候.考虑一下数据源的状况.这时候很有可能被其他人更新了

同样原因:
因为DataSet的特性.它是与数据源脱机的
不管你用什么办法去做本地更改
举个简单例子吧:
插入的值,(先认为数据库自增是从1开始,没有数据)
1,"a"
2,"b"
3,"c"
4,"d"
5,"e" <---这些数据是没问题
但是注意.这时候还没Update.还在你本地的DataTable里
假如这过程有另一个客户端执行了.Insert命令.
数据库有一条记录了(1,"other")
那你的本地数据是无论如何也更新不进去的.(因为1重复了)
正因为如此.DataTable不可能为你做自增的序列.尤其是在这种情况下:
你有两个客户端同时运行
第一个 第二个
3 4
4 5
5 6
6 7
每个人并不是随时与数据源同步的,DataTable也不可能为你跳着添加这个序列啊?
不可能知道另外的人还没有Update的数据序列到了什么位置?

基于这种情况.
可以说MS的开发人员在设计DataSet的时候已经考虑到这问题了(DataSet的要求)
它的设计目的决定了.它无法自动完成这些要求.出错是必然的

说了这些不知道你明白没有
解决办法可能就是你要自己手工改一下Adapter的Insert,Update.Delete等这些命令
mooniscrazy 2003-08-24
  • 打赏
  • 举报
回复
你何不把这段代码拷贝下来试验一下,看看代码有什么问题?应该怎么改?这段代码极其简单,我想不可能有什么问题。自动编号出错,一般是在表为空的时候,同时插入多条记录时候发生。以后一般是正常的,但是有时候遇到某种数据,会表现出来,而且一旦表现出来,就会连续出错。我建议大家试验一下,毕竟不是我一个人的问题。你们也可能遇到。没有遇到,只能说你们运气好。我试验了好几天,每次都会出错。附带说一下,我用的是sqlserver 2000+sp3,visual studio 2003,vs 2002也有同样的问题。
jiezhi 2003-08-24
  • 打赏
  • 举报
回复
我們使用了大量的自動編號,但沒有什么問題。
我想應該是你代碼的問題。
pretender1982 2003-08-24
  • 打赏
  • 举报
回复
告诉Bill
csharplove 2003-08-24
  • 打赏
  • 举报
回复
如果确认是BUG,最好反馈到微软,希望能使.NET更完善
cnhgj 2003-08-24
  • 打赏
  • 举报
回复
UP
coudoufu 2003-08-24
  • 打赏
  • 举报
回复
up
Maxdell 2003-08-24
  • 打赏
  • 举报
回复
UP
wd_318 2003-08-24
  • 打赏
  • 举报
回复
UP
wjcking 2003-08-24
  • 打赏
  • 举报
回复
看看

110,532

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧