前两天在开发一个论坛的时候发现了这样一个问题:如何在动态显示栏目组的时候动态显示每个栏目组相对应的栏目项。很久以前这个就考虑到这个问题,苦于没有解决办法,到网上搜索了一下,居然发现了很多篇文章,
http://blog.csdn.net/whpu/archive/2006/04/30/698344.aspx 这个是其中一个,只是很郁闷我发现所有的网站上对于该问题的解决方法都一样,并且似乎出于同一篇文章,并且无源克查询(中国盗版都到这种程度了,真是有点感叹)。可是他们的解决方案不够好,为什么。先分析一下他们的解决方案的原理:ConnectToDataBase()->OuterRepeater.DataBind()->OutreRepeater.DataItemCommand()->FindInnerRepeater()->ConnectToDataBase()->InnerRepeater.DataBind()。这样的方式效率很低,为什么?Web的瓶颈在于对数据库的访问,我们假想OuterRepeater有10个重复项,这意味着我们需要再次连接到数据库10次,并且还要通过反射再次邦定,这样的效率不低才怪呢!那么该如何高效的解决这个问题。
解决这个问题的方法的前提是有SQLServer2005,因为SQLServer2005能够输出将数据查询转变成XML并且格式化输出。先看看数据是如何输出XML的吧。现在假定数据库里有两个表ForumGroup和ForumColumn,通过ForumGroup.id = ForumColumn.forumGroupID关联起来。下面是查询代码:
SELECT fg.[name],fc.[name],fc.description
FROM ForumGroup AS fg,ForumColumn AS fc
WHERE fc.forumGroupID = fg.id
ORDER BY fg.id ASC
FOR XML AUTO,Elements,ROOT('ForumGroups')
其生成的XML如下:
<ForumGroups>
<fg>
<name>交流与宣传</name>
<fc><name>业界快讯</name><description>跟踪IT动态,评述业内时事,预测未来走向。</description></fc>
<fc><name>资源共享</name><description>聚集资源,让大家一起共享,乐人乐己。</description></fc>
</fg>
<fg>
<name>需求与开发</name>
<fc><name>交互性课件制作</name><description>物理现象模拟,函数图形绘制,化学试验模拟,分子三维结构模拟。</description></fc>
<fc><name>网络组件开发</name><description>ASP.NET和J2EE组件开发。</description></fc>
</fg>
</ForumGroups>
好,到目前为止还是一个什么都不能做的代码,剩下的问题是如何进行数据邦定。可能高级别的用户想重写UserCompoent的Render方法,自己解析XML,然后输出Html,这样是十分高效的,但是不好的就是不利于可视化开发、维护和扩展。那么可能有见识的读者就想到了使用XPath进行邦定,不错,这是我们的解决方法,但是如何进行InnerRepeater的邦定呢。其实在MSN上有个解决方案:使用XPathSelect来选择数据源,然后再邦定,但是其OuterRepeater是使用XMLDataSource,这意味着数据是固定的(实际上可以通过动态赋值给Data属性的方法,但并不需要这样做),但是我们的数据往往需要变化,是从数据库里面查询来的,那么该如何解决呢?先看下面的选择数据源的代码:
// R: OuterRepeater
// "this.CommandLoadForumGroups.ExecuteXmlReader()" returns a XMLReader after connecting to the database.
XPathDocument document = new XPathDocument(this.CommandLoadForumGroups.ExecuteXmlReader());
this.R.DataSource = document.CreateNavigator().Select("/ForumGroups/fg");
this.R.DataBind();
好,逻辑层代码已经完成,注意上面第二行注释,表示这是一个已经写好的函数,实际上就是一个SqlCommand,知道这个的读者应该很快能理解,我就不在此啰嗦了。下面是表现层代码:
<asp:Repeater ID="R" runat="server">
<ItemTemplate>
<table style="width: 100%" border="1px">
<caption>
<%# XPath("name") %>
</caption>
<asp:Repeater ID="R1" DataSource='<%#
((XPathNavigator)Container.DataItem).Select("fc") %>' runat="server">
<ItemTemplate>
<tr>
<td>
<%# XPath("name")%>
<br />
<%# XPath("description")%>
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
</table>
</ItemTemplate>
</asp:Repeater>
注意上面粗体的地方,该地方不是使用XPathSelect,因为XPathSelect只能解析XMLNodes,而此处实际上应该是XMLPathNavigator,因此会出错,所以我们才使用了下面的代码:((XPathNavigator)Container.DataItem).Select("fc")。好了所有的问题都解决了,等等,这样编译器通不过,呵呵,别忘了加个命名空间:<%@ Import Namespace="System.Xml.XPath" %>。
好了,就是这样的解决方法,该方法非常的高效,可以看到这种方式使得我们连接数据库只有一次,并且在进行数据邦定的时候并不是使用的反射机制,而是使用的XML解析,这样效率自然就高了。当然如果是Access呢?或者SQL2000呢?虽然不能这么直接,但是还是有办法的,如果你掌握了这个思想。有个很简单的方法,那就是使用返回为DataSet,然后调用WriteXML()方法,就能得到一个流,再转变成XMLDocument,剩下的事情就是一样的了。
今天就到这里了,有什么不明白的就发我Email或在下面发表回复啊。