在WinForm或WebFrom中都会使用到TreeView控件来显示组织机构信息、目录结构信息及其他信息,不可避免的要与数据库连接,动态的生成树,有的是两节树,有的是三节树,然而随着数据库数据的不断增加,绑定TreeView的效率就成为了不可忽视的问题。如何高效的绑定TreeView就成为了开发人员一个比较关心的问题?日前,我之前参与的项目也使用到了TreeView,也同样的遇到了这个问题。下面就来分析我的解决方案,供大家参考,不足之处敬请原谅!
设计数据库:
数据表:工程信息表(ProjectInfo)、建筑物信息表(BuildingInfo)、点表(Point)
具体字段参考实际表
WinForm项目
开发环境:VS2008 + Access
机器配置:2G内存,2.31GHz,AMD Athlon(tm)64 X2 Dual Core Processor 4400+
测试数据:工程(30)、建筑物(每个工程30个建筑物)、点(1个)
思路
第一种思路:
1、 先从数据库中查询所有工程信息,依次添加工程节点;
2、 根据绑定的工程节点查询所有对应建筑物信息,依次添加建筑物节点;
3、 根据绑定的建筑物节点查询所有对应点信息,依次添加点节点
第一种思路不是本文具体讨论的思路,不做详细的解释。利用以上测试数据绑定TreeView所花时间为10s左右。枪毙第一种思路,数据量越大所花时间越多,这是不能接受的。
第二种思路:
1、先从数据库中查询所有工程信息,依次添加工程节点,绑定TreeView;
2、在TreeView上点击选择工程节点,根据工程节点依次绑定建筑物节点;
3、待建筑物节点绑定完毕后,再点击选择建筑物节点,根据建筑物节点依次绑定点节点;
第二种思路绑定TreeView的时间的确减少不少,但是这种思路仅供大家参考,本人在实际项目开发过程中根据业务需求没有使用这种方法。所以也不做具体的解释。
经分析利用第一中思路之所以所花时间这么长,是因为对数据库的操作太过频繁,连接数据库、打开数据库、执行命令次数太多(900次)。基于这一点,我和公司的同事讨论了下,我们最后的方案是:用空间换时间。具体怎么换请参考第三种思路:
第三种思路:
1、 依次查询出所有的工程信息、建筑物信息、点信息;
2、 建立数据集(DataSet)添加信息结果表(proTable)、建筑物信息结果表(bulTable)、点信息结果表(potTable);
3、 建立关系(DataRelation)
经过以上三步之后我们所需要的所有数据都装载在DataSet集合中,根据业务需求直接从DataSet取数据,减少操作数据库的次数(3次)
4、 依次绑定绑定工程节点;
5、 根据工程结果表与建筑物结果表的关系,依次绑定工程所对应的建筑物信息;
6、 根据建筑物结果表与点结果表的关系,依次绑定建筑物所对应的点信息;
核心步骤大致是这几个步骤,同样的利用上面的测试数据进行测试,绑定整个树的时间能控制在1s以内(大约是0.4s)。为什么会这样呢,因为这样的思路我们只对数据库一次性操作了3次,我们所需的绑定数据都是从DataSet中直接存储,再也不直接操作数据库了,大家可能就有疑惑了,具体的这么直接操作DataSet呢,这不急我们第三种思路的5、6两步不是为结果表建立了关系吗,我们直接通过关系(好比数据库中的主外键)来操作DataSet,其实我的理解是这样操作与我们第一种思路操作数据的原理是一样,只不过一个是在数据库,而另一个则是DataSet,我们直接在程序中操作DataSet。但是有个问题是这样的思路增加内存的消耗。这样,程序就实现了空间换时间的效果。
Code:
//为绑定树形控件提供数据
private DataSet GetTableInfo()
{
DataTable proTable = Null;
DataTable bulTable = Null;
DataTable potTable = Null;
//准备数据集
DataSet ds = new DataSet();
//准备SQL语句
string str_sql1 = "select * from ProjectInfo ";
string str_sql1 = "select * from BuildingInfo ";
string str_sql1 = "select * from Point ";
//执行命令返回数据表
proTable = OledbHelper.ExecuteDataTable(Unlity.OledbHelper.str_connectionString, CommandType.Text, str_sql1, null);
proTable.TableName = "pro"; //为了防止dataset里存在相同的表
bulTable = OledbHelper.ExecuteDataTable(Unlity.OledbHelper.str_connectionString, CommandType.Text, str_sql2, null);
bulTable .TableName = "bul"; //为了防止dataset里存在相同的表
potTable = OledbHelper.ExecuteDataTable(Unlity.OledbHelper.str_connectionString, CommandType.Text, str_sql3, null);
potTable.TableName = "pot"; //为了防止dataset里存在相同的表
//添加结果表到ds
ds.Tables. AddRange (new Table[]{pro_table.Copy(),table1.Copy(),table2.Copy() });
//为结果表建立关系工程与建筑物关系
DataRelation pro_bul=new DataRelation(“proBuilding”,ds.Tables[“pro”].Columns[“project_Id”]
,ds.Tables[“bul”].Columns[“project_id”],false);
//false:代表关系不创建约束
//为结果表建立关系建筑物与点关系
DataRelation bul_pot=new DataRelation(“bulPoint”,ds.Tables[“bul”].Columns[“buling_Id”]
,ds.Tables[“pot”].Columns[“buling_Id”],false);
//添加关系到ds
ds.Relations.AddRange(new DataRelation[]{pro_bul , bul_pot);
//返回ds
retrun ds;
}
//绑定TreeView
Private void DataBindTv()
{
if (tv.Nodes.Count > 0)
{
tv.Nodes.Clear();
}
DataSet ds = GetTableInfo();
TreeNode proNode,bulNode,potNode;
If(ds.Tables[pro].Rows.Count > 0)
{
//绑定工程节点
proNode = new TreeNode();
foreach(DataRow proDr in ds.Tables[pro].Rows.)
{
If(proDr.ItemArray[0].ToString().Trim()!=””)
{
//添加工程节点的text
pro_node.Text = pro_dr.ItemArray[2].ToString().Trim();
//添加工程节点的tag
pro_node.Tag = pro_dr.ItemArray[0].ToString().Trim();
//添加工程节点
tv.Nodes.Add(pro_node);
//根据工程与建筑物对应关系查询出工程对应的所有建筑物的信息foreach(DataRow bulDr in proDr.GetChildRows(“proBuilding”))
{
//判断建筑物编号不为空
If(bulDr.ItemArray[0].ToString().Trim()!=””)
{
bulNode = new TreeNode();
//建筑物节点文本
bulNode.Text = bulDr.ItemArray[1].ToString().Trim();
//建筑物节点tag
bulNode.Tag = bulDr.ItemArray[0].ToString().Trim();
//为工程添加建筑物节点
proNode.Nodes.Add(bulNode);
//根据建筑物与观测点对应的关系查询出建筑物对应所有的观测点信息
foreach(DataRow potDr in bulDr.GetChildRows(“bulPoint”))
{
if(potDr.ItemArray[0].ToString().Trim() !=””)
{
potNode = new TreeNode();
//点节点文本
potNode.Text = potDr.ItemArray[0].ToString().Trim();
//点节点Tag
proNode.Tag = potDr.ItemArray[1].ToString().Trim();
bulNode.Nodes.Add(potNode);
}
}
}
}
}
}
}
备注:程序中ItemArray[0] ItemArray[1]的0和1代表是相应的列的值(一个是名称,另一个是编号)
纵观整段代码:核心代码只有几段
第一:数据结果表建立关系
第二:根据关系查询关系所对应的结果信息
核心代码基本就这么多,大家可以根据自己的需要来优化代码,如果大家有什么更好的方法大家可以一起讨论讨论