在Portal Starter Kit中,用嵌套DataList实现了如上图所示的问题及答复列表,感觉不错,一种实现思路。特将该实现思路摘取出来,当然其中也存在一些小问题,不过基本上还是不错的。
1.数据表的设计
在该实现中,只设计了一张表Portal_discussion。在一般的类似功能的实现中,一般是两种表:一个表存放的是顶层问题描述表,另一个存放的是问题的回复。但在Portal Starter中,只设计了Portal_discussion一个表,在该表中同时存放了问题以及问题的回复。表结构如下图所示。
其中,ItemID为自增列;ModuleID为模块ID。其最重要应用的是字段DisplayOrder,我们可以发现
该字段是nvarchar(750),从字段的英文名字上,我们可以将其翻译为“显示顺序”。那么用该字段是怎么实现的显示顺序的呢?其实,在该字段中存放的是该问题上层问题的DisplayOrder值+本次发表问题的时间,所形成的新的字符串值(日期),如下图所示。
2.DataList
该功能的实现主要利用了嵌套DataList来实现的。代码如下:
0 2 < ItemTemplate >
0 3 < asp:ImageButton id ="btnSelect" ImageUrl='<%# NodeImage((int)DataBinder.Eval(Container.DataItem, "ChildCount")) % >' CommandName="select" runat="server" />
0 4 < asp:hyperlink Text='<%# DataBinder.Eval(Container.DataItem, "Title") % >' NavigateUrl=' < %# FormatUrl((int)DataBinder.Eval(Container.DataItem, "ItemID")) % >' Target="_new" runat="server" ID="Hyperlink1" />,
0 5 from
0 6 < %# DataBinder.Eval(Container.DataItem,"CreatedByUser") % >
0 7 , posted
0 8 < %# DataBinder.Eval(Container.DataItem,"CreatedDate", "{0:g}") % >
0 9 </ItemTemplate>
10 < SelectedItemTemplate >
11 < asp:ImageButton id ="btnCollapse" ImageUrl ="~/images/minus.gif" runat ="server" CommandName ="collapse" />
12 < asp:hyperlink Text='<%# DataBinder.Eval(Container.DataItem, "Title") % >' NavigateUrl=' < %# FormatUrl((int)DataBinder.Eval(Container.DataItem, "ItemID")) % >' Target="_new" runat="server" ID="Hyperlink2" />,
13 from
14 < %# DataBinder.Eval(Container.DataItem,"CreatedByUser") % >
15 , posted
16 < %# DataBinder.Eval(Container.DataItem,"CreatedDate", "{0:g}") % >
17 < asp:DataList id ="DetailList" ItemStyle-Cssclass ="Normal" datasource="<%# GetThreadMessages() % >" runat="server">
18 < ItemTemplate >
19 < %# DataBinder.Eval(Container.DataItem, "Indent") % >
20 < img src="<%=Global.GetApplicationPath(Request)% >/images/1x1.gif" height="15">
21 < asp:hyperlink Text='<%# DataBinder.Eval(Container.DataItem, "Title") % >' NavigateUrl=' < %# FormatUrl((int)DataBinder.Eval(Container.DataItem, "ItemID")) % >' Target="_new" runat="server" ID="Hyperlink3" />,
22 from
23 < %# DataBinder.Eval(Container.DataItem,"CreatedByUser") % >
24 , posted
25 < %# DataBinder.Eval(Container.DataItem,"CreatedDate", "{0:g}") % >
26 </ItemTemplate>
27 </asp:DataList>
28 </SelectedItemTemplate>
29 </asp:datalist>
3.三个重要的SQL存储过程
1.增加一个问题。不管增加的是顶层问题或是对某个问题的回复问题,都可以利用该存储过程。
0 2 (
0 3 @ItemID int OUTPUT ,
0 4 @Title nvarchar (100 ) ,
0 5 @Body nvarchar (3000 ) ,
0 6 @ParentID int ,
0 7 @UserName nvarchar (100 ) ,
0 8 @ModuleID int
0 9 )
10
11 AS
12 / * Find DisplayOrder of parent item * /
13 DECLARE @ParentDisplayOrder as nvarchar (750 )
14 SET @ParentDisplayOrder = ""
15
16 SELECT
17 @ParentDisplayOrder = DisplayOrder
18 FROM Portal_Discussion
19 WHERE
20 ItemID = @ParentID
21
22 INSERT INTO Portal_Discussion
23 (
24 Title ,
25 Body ,
26 DisplayOrder ,
27 CreatedDate ,
28 CreatedByUser ,
29 ModuleID
30 )
31
32 VALUES
33 (
34 @Title ,
35 @Body ,
36 @ParentDisplayOrder + CONVERT ( nvarchar (24 ) , GetDate ( ) , 21 ) ,
37 GetDate ( ) ,
38 @UserName ,
39 @ModuleID
40 )
41
42 SELECT
43 @ItemID = @@Identity
44
45 GO
【说明】该存储过程中,对于参数@ParentID的值,如果是新增的问题,@ParentID=0;如果是回复某个问题的,那么@ParentID=那个问题的ItemID。该存储过程的实现思路是:
(1)声明一个@ParentDisplayOrder as nvarchar(750)
(2)根据@ParentID的值来,查找父问题的DisplayOrder字段的值,并存储到@ParentDisplayOrder变量中
(3)向表中插入数据。其中的关键是在DisplayOrder字段的赋值时,@ParentDisplayOrder + CONVERT( nvarchar(24), GetDate(), 21 )。其中CONVERT是用来将GetDate()获得日期做了格式化,会返回类似如下值:2009-01-22 23:16:09.807(长度为23,下面的获取顶层问题列表的存储过程会用到)。
(4)最后将新增加的问题的ItemID返回。
2.获取顶层问题列表;
0 2 (
0 3 @ModuleID int
0 4 )
0 5 AS
0 6
0 7 SELECT
0 8 ItemID ,
0 9 DisplayOrder ,
10 LEFT (DisplayOrder , 23 ) AS Parent ,
11 ( SELECT COUNT ( * ) -1 FROM Portal_Discussion Disc2 WHERE LEFT (Disc2 .DisplayOrder , LEN ( RTRIM (Disc .DisplayOrder ) ) ) = Disc .DisplayOrder ) AS ChildCount ,
12 Title ,
13 CreatedByUser ,
14 CreatedDate
15
16 FROM Portal_Discussion Disc
17
18 WHERE
19 ModuleID =@ModuleID
20 AND
21 ( LEN ( DisplayOrder ) / 23 ) = 1
22
23 ORDER BY
24 DisplayOrder
25
26 GO
由于在该系统中,顶层问题,以及对顶层问题的回复都在一个表里,而在显示的时候,我们需要只显示顶层的问题列表,所以,该存储过程的主要目的是获取顶层问题列表。该存储过程的返回值,包括了顶层问题的基本信息,同时还包含了一个新的字段ChildCount,返回的是每个顶层问题的回复数量。用于控制在DataList中,图标的显示。回复数量>0的显示一个+号图标,没有回复数量的显示点图标。主要的是看明白这句:
3.获取某个指定问题。
该存储过程根据@ItemID参数,获取相应问题的信息。其中还包括了两个新字段信息。NextMessageID和PrevMessageID。
(
@ItemID int
)
AS
DECLARE @nextMessageID int
EXECUTE Portal_GetNextMessageID @ItemID , @nextMessageID OUTPUT
DECLARE @prevMessageID int
EXECUTE Portal_GetPrevMessageID @ItemID , @prevMessageID OUTPUT
SELECT
ItemID ,
ModuleID ,
Title ,
CreatedByUser ,
CreatedDate ,
Body ,
DisplayOrder ,
NextMessageID = @nextMessageID ,
PrevMessageID = @prevMessageID
FROM Portal_Discussion
WHERE
ItemID = @ItemID
其中获取NextMessageID(PreMessageID类似)的存储过程如下:
0 2 CREATE PROCEDURE Portal_GetNextMessageID
0 3 (
0 4 @ItemID int ,
0 5 @NextID int OUTPUT
0 6 )
0 7 AS
0 8
0 9 DECLARE @CurrentDisplayOrder as nvarchar (750 )
10 DECLARE @CurrentModule as int
11
12 / * Find DisplayOrder of current item * /
13 SELECT
14 @CurrentDisplayOrder = DisplayOrder ,
15 @CurrentModule = ModuleID
16 FROM Portal_Discussion
17 WHERE
18 ItemID = @ItemID
19
20 / * Get the next message in the same module * /
21 SELECT Top 1
22 @NextID = ItemID
23
24 FROM Portal_Discussion
25
26 WHERE
27 DisplayOrder > @CurrentDisplayOrder
28 AND
29 ModuleID = @CurrentModule
30
31 ORDER BY
32 DisplayOrder ASC
33
34 / * end of this thread? * /
35 IF @@Rowcount < 1
36 SET @NextID = null
37
38 GO
其他细节,请看源代码吧。困了,睡觉了。