昨天处理了一个奇怪的duplicated data entry的问题。我们的ASP.NET前台网页里有这么一个HtmlButton控件:

<button runat="server" type="submit" id="yesButton" onserverclick="yesButtonClick">Click this</button>

 

在codebehind的yesButtonClick代码里我们会向一个web service发送一个请求来添加一个数据项:

protected void yesButtonClick(object sender, EventArgs e) { // send request to the database web service }

 

在独立的UI automation测试的时候我们并没有发现问题。但在stress test的时候,我们发现数据库在很偶然的情况下会有两个一摸一样的数据项,并且他们的creation time stamp也是一摸一样的。而我们一直以为所有的数据项都是可以通过一个data field来唯一确认的,所以重复的data entry会给后来的程序逻辑带来很大的麻烦。

很显然在stress test的时候,我们在更新数据库的时候触发了很不容易发现的race condition而导致两个添加数据库记录的同时被执行了,而数据库方面也没有primary key这样的唯一性支持,而只是在添加一条记录前检查一下表内是否已经有了同样的一条记录,如果有就不添加,如果没有再添加,而这显然是无法避免race condition的,是数据库接口设计上的失误。

关键问题在于为什么我们的ASP.NET会同时触发两个添加记录的请求。研究了一下后才发现原来是yesButtonClick给调用了两次。一次是因为onserverclick(被ASP.NET的引擎转化为这样的客户端javascript: onclick=__doPostBack(...) ),另一次是因为type="submit"这个属性。因为每个aspx页面的最外层都是一个巨大的form,这个form的method是POST,action就是缺省就是页面本身。所以当把一个button的属性设置为submit的时候,button click这个时间就会自动触发一个form post,这样在服务器端,yesButtonClick就又被调用了一次。

解决的方法很简单,只要去掉type="submit"就好了。

我觉得这也许算不上一个bug,因为对ASP.NET引擎来说它生成的都是完全合法的html和javascript。但为什么有submit button引起的form post会在服务器端触发button click event handler是我不解的,或者说,我觉得不应该这样设计,或者至少该有个什么样的警告。而且我可以想见在某些情况下程序员有足够的理由一定要放上type="submit"这个属性,那时候怎么办?只能看着yesButtonClick被调用两次?

希望对以后碰到过同样的问题的朋友有些帮助。