层次数据映射与存储

在开发一个.NET桌面应用程序的内容映射模块时,需要将内容分类成类似层次树的结构,并且需要以多种格式表示这棵树,比如文本、表格、节点和数据库格式。在网上搜索了能够将层次树表示到SQL2005数据库的.NET库,但没能找到。不过,找到了一篇名为"Storing Hierarchical Data in a Database"的文章,作者是Gijs Van Tulder,它解释了MPTT(Modified Preorder Tree Traversal)的概念。不幸的是,这篇文章是为PHP开发者写的,所以将这个概念翻译成了C#和SQL2005代码。此外,还编写了一些算法来将树表示为文本、表格、树视图节点和图形格式。还使用了提供者模型技术作为支持各种数据库(如SQLite)的一步。

最终,将所有这些内容整合到一个项目中,但整个类可以很容易地重用并分离成类库,即使是写的创新算法也可以单独重用,比如用于渲染树的图形表示的算法。现在,认为已经找到了一种有效的方法来添加、插入、删除和检索树元素,包括使用SQL2005数据库的有效模式,即MPTT,它通过每次活动只需一个查询来最小化数据库查询的数量。

什么是层次树?简单来说,它是一种看起来像真实树的数据结构,尽管层次树通常与真实树相反(即根在顶部,叶子在底部),该结构的元素通过父子关系相互关联,这些关系通过连接线表示,称为“分支”,此外,没有上级的元素称为“根”,没有子元素的元素称为“叶子”。

以下示例展示了动物王国树的文本表示:

Animal Kingdom #Backbones ##Mammal ##Lungs ##Reptile ##Bird ##Gills ###Fish ###Amphibian #No Backbones ##Starfish ##Mollusk ###Snail ###Clam ##Jointed Legs ###Insect ###Spider ###Crustacean

因此,有1个根(Animal Kingdom)和12个叶子(Mammal, Lungs, Reptile, Bird, Fish, Amphibian, Starfish, Snail, Clam, Insect, Spider, Crustacean),有18行,每行包含一个树元素/节点,节点标题前面的(#)数量表示元素在树中的深度级别,具有相同级别和相同父级的元素称为“兄弟”。

这种树的图形表示存在于图2中,其中矩形是节点的象征性表示,此外,这种树的表格表示存在于图3中。

使用演示项目:回到上一节,复制图1的文本,并将其粘贴到演示项目界面右侧的TextBox中,然后,按下标题为"<"的按钮,将文本转换为左侧TreeView控件中的传统树表示。

通过点击名为"Tabular"的单选按钮显示树的表格表示,并使用名为"Textual"的另一个单选按钮在文本和表格之间切换。

要将刚刚转换的树保存到SQL2005数据库,应该首先选择一个现有的数据库,但不必创建任何表,只需通过点击名为"Connection String"的按钮构建一个有效的连接字符串,构建连接字符串后,只需点击名为"Save"的按钮即可将树保存到名为"tblTree"的表中,如果该表不存在,则会自动创建;如果已存在,则会被截断。

当然,可以随时从相同的连接字符串重新加载这棵树,只要正确提供了它,只需编写连接字符串并点击"Load"按钮。但是,不必每次打开演示项目时都输入连接字符串,它会将连接字符串自动保存到名为constring.txt的文件中。

现在,可以通过点击"Draw"按钮来享受树的图形表示。

使用代码:只需要创建一个MpttCoreEngine类的实例并调用其接口中的任何方法。

MpttCoreEngine engine = new MpttCoreEngine();

这个类包含了所有编写的算法,用于以各种格式表示层次树,以下列表包含了这个类中最重要的方法。

SetConnectionString:使用此函数为引擎提供连接字符串 ConvertMpttIntoTree:使用此函数从数据库加载树节点 ConvertTreeIntoMptt:使用此函数将树节点保存到数据库 ConvertTextIntoTree:将文本树转换为适合TreeView的TreeNode ConvertTextTableIntoTree:将表格树转换为适合TreeView的TreeNode ConvertTreeIntoText:将TreeNode转换回文本表示 ConvertTreeIntoTable:将TreeNode转换回表格表示 DrawTree:在Image上绘制TreeNode并返回Image以供后续使用

以下代码是从演示项目中截取的,展示了如何使用这些方法。

private void LoadDb() { try { treeView.Nodes.Clear(); TreeNode node = null; engine.SetConnectionString(_connectionString); node = engine.ConvertMpttIntoTree(); if (node != null) { treeView.Nodes.Add(node); treeView.ExpandAll(); } } catch (Exception e) { MessageBox.Show(e.Message + "\r\nThe Connection string may not be correct"); } }

代码充满了有趣的点,其中大多数可能需要单独的文章来解释。这里是最重要的一些:

绘制树节点的算法非常复杂,使用了匿名函数、递归和回调来计算节点的准确位置。可以在DrawTree函数中找到该算法。

MPTT概念在互联网上的许多地方都有解释,不过,将这个概念与一个称为"提供者模型"的设计模式结合起来,创建了一个名为TreeProvider的抽象类,用于实现并支持任何数据库类型,如SQL2005、MySQL或SQLite。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485