用的数据库是从中国省市县数据库2009年1月版.mdb(http://download.csdn.net/source/975699)里导出来的,2009年1月版,是因为发布日期是2009-01-18 01:44,这个数据库的数据是什么时候整理的我不知道,我用的数据是从这里导出来的,我按我要的格式导到了MS SQL 2000里
昨天大猫说数据不准确,我从华通数据中心里截了三个页面,还没按这个重新整理过数据:
这个是我的数据库的两个文件,需要的人可以直接在SQL里附加/uploadfile/2010/01/20/20100120124430671.rar
这个是晚的数据库导出后的txt文件,不用SQL的人随便导哪个自己的数据库里都可以/uploadfile/2010/01/20/20100120124445734.rar
数据库里有两个表,DicArea用于三级联动,DicCity用于二级联动,二级联动我用的是js脚本,从数据库里生成一个js脚本后,在网站里直接用js脚本,三级联动我用的是数据库,直接用Ajax读取数据库
用的是vs.net2005,没有导入非官方Ajax控件,所以全部东西是自己写的,不要和我说建议vs.net2008,我知道在2008里怎么搞这个,就是,已经用2005写好的项目不可以因为一个问题全部升到2008的
全部测试代码:/uploadfile/2010/01/20/20100120130117468.rar
记录网址:
因为DropDownList在用客户端修改过option(ListItem)后,会导致“回发或回调参数无效”的错误,所以一直在用各种办法解决这个问题,甚至用过在服务器端先把所有数据读取出来注册好后,再用js清空添加需要的数据这样子的办法,这个办法好处是代码好简单,坏处是,要在页面一开始时,读取所有数据,如果是三级联动,县的数据有3000多。。。数据量太大了
现在使用的办法是
解决不到就避开
我用了客户端控件select给用户操作,用了HiddenField在select每一次初始化和选项变更时记录当前的选项值,用来和服务器交互,我把服务器控件DropDownList扔掉了
先说三级联动,文件一共四个:
脚本文件:Area.js
自定义控件:AreaAjax.ascx (AreaAjax.ascx.cs)
获取市县列表的后台文件:AreaAjaxList.aspx (AreaAjaxList.aspx.cs)
测试调用自定义控件:TestUoAreaList.aspx(TestUoAreaList.aspx.cs)
先看自定义控件:AreaAjax.ascx,在这个控件里,我要做的是三级联动时,允许数据绑定到县,用于注册用户或者修改用户资料,我还要在搜索时,只搜索到市一级,不显示县级数据
AreaAjax.ascx
<script type="text/javascript" src="/MyUo/Area.js"></script>
<select name="aProvince" onchange="setCityList(this.value, '');" id="aProvince"></select>
<select name="aCity" onchange="setCountyList(this.value, '');" id="aCity"></select>
<asp:Literal ID="LtCountySelect" runat="server" Text="<select name='aCounty' onchange='setCounty(this.value);' id='aCounty'></select>"></asp:Literal>
<asp:HiddenField ID="HfProvince" runat="server" /><asp:HiddenField ID="HfCity" runat="server" /><asp:HiddenField ID="HfCounty" runat="server" />
<asp:HiddenField ID="HfPList" runat="server" /><asp:HiddenField ID="HfCList" runat="server" /><asp:HiddenField ID="HfOlist" runat="server" />
<script type="text/javascript">
//三个选项的客户端控件,用于和用户交互
var objProvince = document.getElementById("aProvince");
var objCity = document.getElementById("aCity");
var objCounty = document.getElementById("aCounty");
//三个选项的服务器端控件,用于和服务器交互
var hiProvince = document.getElementById("<% =HfProvince.ClientID %>");
var hiCity = document.getElementById("<% =HfCity.ClientID %>");
var hiCounty = document.getElementById("<% =HfCounty.ClientID %>");
//三个记录当前选项列表的服务器控件,用于避开页面自提交时重复检查数据
var hiPList = document.getElementById("<% =HfPList.ClientID %>");
var hiCList = document.getElementById("<% =HfCList.ClientID %>");
var hiOList = document.getElementById("<% =HfOlist.ClientID %>");
</script>
看红色那一行,这一行是用于显示县的数据,为了实现按需求显示,在这里我用了Literal控件,从服务器端写入select控件,同时控件display属性
AreaAjax.ascx.cs
/// <summary>
/// 设置是否显示县
/// </summary>
public bool ShowCounty
{
set
{
if (value)
{
LtCountySelect.Text = "<select name='aCounty' onchange='setCounty(this.value);' id='aCounty'></select>";
}
else
{
LtCountySelect.Text = "<select name='aCounty' onchange='setCounty(this.value);' id='aCounty' style='display:none;'></select>";
}
}
}
/// <summary>
/// 获取市编号
/// </summary>
public string CityNum
{
get { return HfCity.Value; }
}
/// <summary>
/// 地区编号,对应县的编号
/// </summary>
public string AreaNum
{
get { return HfCounty.Value; }
set
{
HfProvince.Value = value.Substring(0, 2);
HfCity.Value = value.Substring(0, 4);
HfCounty.Value = value;
}
}
protected void Page_Load(object sender, EventArgs e)
{
}
这个控件做的很简单的,就是用了三组控件:
一组三个select控件用于给用户操作选择
一组三个HiddenField控件用户记录用户每一次的选项,这样可以在服务器端直接读取控件的值
最后三个HiddenField控件用于记录每一次从数据库里检索出来的数据列表,在客户端用js修改过的所有数据,在页面提交后(点了搜索按钮、由自提交的控件触发等)都会消失的,每一次页面重新Load,都要重新设置客户端控件,就是,我不想重复读取数据库,所以我把列表记录下来
所有控件都要在客户端由脚本操作,所以我分别又建了9个变量,用document.getElementById获取他们对应的控件,这9个变量相当于全局变量,可以在js脚本里直接调用
下面看脚本文件Area.js
//初始化省
function setProvince()
{
if(hiPList.value == "")
{
//检索省,并记录显示
var pList = GetList(1, "");
hiPList.value = pList;
addOption(objProvince, hiProvince, pList);
//同时初始化市和县
setCityList(objProvince.value);
}
else
{
//由于页面自提交引起的select控件数据丢失,重写,但不重新读取数据库
addOption(objProvince, hiProvince, hiPList.value);
addOption(objCity, hiCity, hiCList.value);
addOption(objCounty, hiCounty, hiOList.value);
}
}
//根据省初始化市:所属省
function setCityList(province)
{
//记录省
hiProvince.value = province;
//检索市
var cList = GetList(2, province);
hiCList.value = cList;
addOption(objCity, hiCity, cList);
//重新检索县
setCountyList(objCity.value);
}
//根据市初始化县:所属市
function setCountyList(city)
{
//记录市
hiCity.value = city;
//检索县
var oList = GetList(3, city);
hiOList.value = oList;
addOption(objCounty, hiCounty, oList);
}
//记录县
function setCounty(couty)
{
hiCounty.value = couty;
}
//获取所有省的列表:列表类别(1省,2市,3县)、父级编号(省为"",市为省,县为市)
function GetList(listType, parentNum)
{
//参数
var submitPage, parmAndValue, list;
submitPage = "/MyUo/AreaAjaxList.aspx";
parmAndValue = "listType=" + listType + "&parentNum=" + parentNum;
//提交
list = submitData(submitPage, parmAndValue);
//返回结果
if(list.indexOf("<") == 0) list = "";
return list;
}
addLoadEvent(setProvince);
//给控件加入选项:要修改选项的select控件、记录这个select控件的默认选项值的控件、所有option的列表,格式:北京市|11,天津市|22
function addOption(objSelect, objValue, optionList)
{
var len, index, value;
var optionArray = new Array();
var oneOption = new Array();
optionArray = optionList.split(',');
len = optionArray.length;
//获取默认值
index = -1;
value = objValue.value;
//清空所有选项
objSelect.options.length = 0;
//把列表加为选项
for(var i = 0; i < len; i++)
{
oneOption = optionArray[i].split('|');
if(value == oneOption[1]) index = i;
objSelect.options[i] = new Option(oneOption[0], oneOption[1]);
}
//设置默认值
if(index == -1) index = 0;
objSelect.options[index].selected = true;
//记录默认值
objValue.value = objSelect.options[index].value;
}
//提交数据:参数、地址
function submitData(submitPage, parmAndValue)
{
//使用表单请求页面
request = new createXMLHttpRequest();
request.open("POST", submitPage, false);
request.setRequestHeader("Content-Type","application/x-www-form-urlencoded")
request.send(parmAndValue);
//取返回的字条串返回
var strResult = request.responseText;
return strResult;
}
//创建XMLHttpRequest对象
function createXMLHttpRequest() //创建XMLHttpRequest对象
{
if (window.ActiveXObject) // IE下创建XMLHTTPREQUEST
{
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
else if (window.XMLHttpRequest) // 其他浏览器创建XMLHTTPREQUEST
{
xhr = new XMLHttpRequest();
}
return xhr;
}
//给页面添加load方法
function addLoadEvent(func)
{
var oldonload = window.onload;
if(typeof window.onload != 'function')
{
window.onload = func;
}
else
{
window.onload = function()
{
oldonload();
func();
}
}
}
最后蓝色的这三个方法是通用方法,这里我只需要提交数字型数据,所以不需要在submitData对数据加密,如果要提交中文数据,关于乱码的问题看这里:http://www.nnllok.cn/Read.aspx?id=401
红色的addOption方法,是用于按我读取出来的数据格式给select加入option的,我的数据格式是:地区描述|地区编号,地区描述|地区编号,……,比如:北京市|11,天津市|22
在这个方法里,我先用split方法按“,”分割,把所有选项加入optionArray数组,然后清空select控件后,循环这个数组,按“|”分割,把每一个选项的值和文件记录在oneOption数组里,把这个选项添加到select中,最后把select的默认选项记录在一个hidden里
最开头的三个方法,就是用于设置省、市、县三个select的了,我在页面onload时,执行setProvince方法,初始化省,因为是联动的,所以在这个方法的最后,我调用setCityList重设了市,在setCityList方法里,设置完市后,我又调用了setCountyList重设了县,这就是联动啊,对不对?一个带着一个跑的,像接力跑,跑完省了,棒棒给了市,轮到市的跑,市跑完了,棒棒给了县,轮到县的跑,县跑完了,直接记下自己的值就可以了,就像全部接力跑完了,跑到领奖台上拿个牌牌就行了啊
绿色的GetList方法,是用于从服务器获取数据的,省市县三组数据都用这个方法获取,所以我要传递不同的参数,listType是列表类别,省是1,市是2,县是3,parentNum是父级编号,省没有父级,所以是"",市的父级是省所以是省的编号,县的父级是市所以是市的编号
数据库中,DicArea.AreaNum是varchar(6),这个字段的前两个字符是省编号,前四个字符是市编号,全部6个字符是市编号
最后就是从后台获取数据的页面了,AreaAjaxList.aspx
<asp:SqlDataSource ID="SdsProvince" runat="server" ConnectionString="<%$ ConnectionStrings:loveString %>" SelectCommand="area_province" SelectCommandType="StoredProcedure"></asp:SqlDataSource>
<asp:SqlDataSource ID="SdsCity" runat="server" ConnectionString="<%$ ConnectionStrings:loveString %>" SelectCommand="area_city" SelectCommandType="StoredProcedure">
<SelectParameters>
<asp:Parameter Name="ProvinceNum" Type="String" />
</SelectParameters>
</asp:SqlDataSource>
<asp:SqlDataSource ID="SdsCounty" runat="server" ConnectionString="<%$ ConnectionStrings:loveString %>" SelectCommand="area_county" SelectCommandType="StoredProcedure">
<SelectParameters>
<asp:Parameter Name="CityNum" Type="String" />
</SelectParameters>
</asp:SqlDataSource>
AreaAjaxList.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
//列表类别(1省,2市,3县)、父级编号(省为"",市为省,县为市)listType=" + listType + "&parentNum
int listType;
string parentNum, tName = null, vName = null;
listType = Convert.ToInt32(Request.Form["listType"]);
parentNum = Request.Form["parentNum"];
//Response.Write("\n" + listType.ToString() + "," + parentNum);
bool ok = true;
DataView dv = new DataView();
try
{
switch (listType)
{
case 1:
dv = (DataView)SdsProvince.Select(new DataSourceSelectArguments());
tName = "Province";
vName = "AreaOrder";
break;
case 2:
//Response.Write(parentNum);
SdsCity.SelectParameters["ProvinceNum"].DefaultValue = parentNum;
dv = (DataView)SdsCity.Select(new DataSourceSelectArguments());
tName = "City";
vName = "AreaOrder";
break;
case 3:
SdsCounty.SelectParameters["CityNum"].DefaultValue = parentNum;
dv = (DataView)SdsCounty.Select(new DataSourceSelectArguments());
tName = "County";
vName = "AreaNum";
break;
}
}
catch (System.Data.SqlClient.SqlException)
{
//Response.Write(ex.Message);
ok = false;
}
if (dv == null || dv.Table == null || dv.Table.Rows.Count == 0)
{
//Response.Write("null");
ok = false;
}
if (ok)
{
bool begin = true;
System.Text.StringBuilder sb = new System.Text.StringBuilder("");
foreach (DataRow dr in dv.Table.Rows)
{
if (begin)
{
begin = false;
sb.Append(dr[tName].ToString() + "|" + dr[vName].ToString());
}
else
{
sb.Append("," + dr[tName].ToString() + "|" + dr[vName].ToString());
}
}
Response.Write(sb.ToString());
}
else
{
Response.Write("");
}
dv = null;
Response.End();
}
这个页面如果我不用数据源控件,直接自己写代码,会简单好多的,一个SqlConnetion、一个SqlCommand、一个DataTable就可以解决问题了。。。就是很难搞清用数据源控件和自己写代码哪里好哪里不好,如果代码少就是好,那为什么vs.net会越出越大,越出越复杂,自带官方控件越来越多呢?
vs.net2008的新控件ListView,他把DataList、FormView、GridView三个的东西集成到一个里了,有好多人说他慢,说他浪费资源,就是也有好多人在用,是因为它可以自己写代码,也可以自动分页和控制数据,我总觉得,如果程序员写程序简单了,如果不是用资源交换的,就是用用户友好度交换的。。。用户的操作简单一步,程序员的工作就会多一堆,如果你不管资源占用,只管完成任务,代码也会少好多好多
对于这个,数据源控件和自己写代码,我不知道哪一种方式更好,在资源占用方面,谁更好,我只是把数据库的连接工作扔级了数据源控件,这里的代码复杂吗?我不知道。。。
我是想,如果我的注册页面,没有用数据控件FormView做,如果我自己添加一堆服务器控件,再加一个注册按钮,在注册的时候,我自己写代码连接数据库,使用事务来注册,保证在检查完用户名有效性后,不间断操作的继续insert数据,这样子我的注册页面就没有bug了,对不对?我只在客户端用Ajax检查了用户重复性,我连在服务器端提交数据后都忘记重新检查了,这是一个bug,好大的bug,注册会员就算不用事务,我也不应该在insert前不对用户名做检查的
现在,因为我没有这样子做,所以大猫开了两个页面,同时注册相同用户名后,我的程序没有判断出来,两个用户都注册成功了,所以我的注册页面有bug,所以大猫说我的代码写得太复杂,可读性太低,所以大猫把我写的全部推翻掉后自己重新写一个。。。。。。。
就是,为什么我们不要在MS SQL数据库里,给用户表的用户名字段加一个索引,或者一个约束呢?为什么不充分利用数据库本来的功能,要自己在代码里对一个insert语句,也使用事务呢?我不懂,我不明白,我不服气,我好难过。。。
大猫第三次这样子做,第一次是一个asp网站,大猫没有催我,我写程序,大猫从来不催我的,那时候,我是给过滤html标签的问题卡住了,我一直搞一直搞,我以为我有好多时间的,等我搞完,天已经黑了,就是,大猫告诉我,他已经自己完好了,不需要我写的东西了。。。大猫没有写过滤html标签的问题,大猫跳过这个问题了,就是,大猫交货了,我写的没有用,写了的东西没有用
还有最近的一个windows服务,一个让多个服务器同时运行,按规定的时间间隔从网络获取数据包后,操作数据库的服务,我写的代码,是一个重要的地方一个try,我只对有可能发生的东西try,我拒绝使用catch(Exception),因为Exception类包含太多东西了,如果程序执行的时候要对每一个东西都检查一下去找对应的异常,这样子太浪费资源了,就是,我把我考虑到的所有异常都加进去了,就是,我的服务还是一直时不时的因为数据库连接返回null值就停止。。。这是一个bug,我一直在修改这个问题,修改了好多次,每一步重要的地方,我都try。。。最后大猫自己重新写,他说我写的太复杂了,他把我的所有try和catch扔掉,他对整个程序使用try,直接catch(Exception),这样子就是说,大猫是让程序的每一个语句,包括定义变量、赋值变量、变量自加之类的,在执行的时候,都要对所有异常检查一遍,长长长长的操作,这个不是在浪费资源吗?我觉得这样子不是最好的办法,找到没有检查出来的异常才是对的,就是,大猫说现在占用的资源不多,大猫说要的是没有bug,要稳定
昨天是第三次,大猫重写我的代码,因为我的注册页面写的太复杂,一堆文件。。。还有最重要的,我有bug,大猫说我这个页面和那个windows服务一样子,修改了不下十次。。。我没有因为bug修改这么多次,我没有,有的修改是因为要求改变才修改的。。。就是到最后,我还是有bug,因为有bug。。。我在把原来自提交检查用户登录名改成用Ajax检查登录名时,把后台检查用户名重复性当成自提交检查的代码删除了,留下了自提交的方法,删掉了后台insert前检查重复性的代码。。。
因为我的代码复杂,很难读得懂,因为我的代码有bug,所以大猫自己重新写注册页面,不要我写的了
以前,asp的时候,我在开始写东西的时候,我总是要先想安全问题,用多少代码我都要保证代码安全,以前,在大家还用电话线上网的时候,我总是想要网站小,占用资源少,有时候为了减小网站,我会用js和css代替图片的特效,就是,现在已经到处都是ADSL了,大家都在用着2M、3M、4M,在用着光纤,大家的机器都已经不是以前的2.5G、6G硬盘,也不是32M、64M、128M内存了,现在的机器硬盘用T算,固体硬盘更加快,现在的内存没有4G别人说太小,现在的CPU都要用双核。。。
是不是,我不应该继续这样子天天看住资源呢?
我记得小狮,小狮是很好的老师,我一直记得他,在PB家园里,他教了我们好多东西,他也是很看重资源占用的,我想小狮肯定也是在没有bug的情况下看重资源的。。。是我把东西搞反了,我先看资源。。。
一个程序员,如果不可以写出稳定的程序,谁还会用你的程序呢?要稳定,要没有bug,因为我做不到这两点,所以大猫自己重新写,也不要我写的,就是,为什么不可以给我把我误删除错的那个代码补回去呢?为什么要自己写,不给我最后一次修改的机会呢?妈妈说大猫是害怕我修改后又有新的bug。。。
为什么会这样子?为什么我要在把自提交验证改成Ajax客户端验证的时候删错代码?我写的东西都没用了。。。我是全世界最没用的人,写什么,bug什么
三级联动的自定义控件就是这么多了,TestUoAreaList.aspx是测试页面来的
二级联动没什么好说的了,就是两个js脚本City.js和CityList.js,调用的页面是city.html,生成CityList.js的文件是CreateCityListJs.aspx全部都在测试包里了
--------------------------------------------------------------
2010-01-21
时光叔叔说:
程序不能有bug,再高的效率也不如一个bug影响大。稳定性和正确性才是最重要的,远远高于效率和技术的先进性。
程序最重要的是稳定和正确,其次要有良好的交互界面和简单的结构,效率在绝大多数情况下不重要,除非是个别场合。
代码复杂会带来效率的提高,但更会带来出错几率的增加,所以一般都是牺牲效率来换取可靠性。
爸爸说:
如果我错了,我不可以等着别人帮我收拾的,工作的时候没有人会像妈妈一样子跟在后面帮我收拾东西,我要自己把错的找出来,自己先修改正确,不可以把自己的责任推给别人,要自己为自己写的程序负责
如果一件新衣服买回来,要改好多地方我才能穿,妈妈宁愿给我重新买一件新的,如果我的程序出好多bug,改了一个bug又出另一个,大猫就宁愿重新自己写了
太小我穿不进的衣服,妈妈肯定不会给我买的,大猫给我工资叫我写程序,我写完了,他用不到,还要自己重新写,大猫是给钱我写他用不到的东西,他买了自己用不到的程序
如果我想要自己的程序有人用,我就要先保证自己的程序不出错,要保证程序不出错,就要用最简单的办法完成任务,使用的方法越复杂,就越容易出错,在开始写代码的时候,要先想好这样子写程序可以不可以稳定运行
一定要记住:程序不能有bug,效率再高不如一个bug影响大,稳定性和正确性是最重要的,远远高于效率和技术的先进性
我说:
大猫好可怜,都是我搞的。。。还要给我叫大坏蛋,为什么大猫不叫回我大坏蛋呢?我想要写好的程序有人用,我不想要别人扔掉我的程序,我要写没有bug的程序,我要在大猫从我的程序里找出bug前,自己先找出来,修改掉