代码改变世界

Portal Starter Kit 学习之-问题答复列表

2009-01-22 23:53  名刘天下  阅读(267)  评论(0编辑  收藏  举报

在Portal Starter Kit中,用嵌套DataList实现了如上图所示的问题及答复列表,感觉不错,一种实现思路。特将该实现思路摘取出来,当然其中也存在一些小问题,不过基本上还是不错的。

1.数据表的设计

在该实现中,只设计了一张表Portal_discussion。在一般的类似功能的实现中,一般是两种表:一个表存放的是顶层问题描述表,另一个存放的是问题的回复。但在Portal Starter中,只设计了Portal_discussion一个表,在该表中同时存放了问题以及问题的回复。表结构如下图所示。

其中,ItemID为自增列;ModuleID为模块ID。其最重要应用的是字段DisplayOrder,我们可以发现

该字段是nvarchar(750),从字段的英文名字上,我们可以将其翻译为“显示顺序”。那么用该字段是怎么实现的显示顺序的呢?其实,在该字段中存放的是该问题上层问题的DisplayOrder值+本次发表问题的时间,所形成的新的字符串值(日期),如下图所示。

2.DataList

该功能的实现主要利用了嵌套DataList来实现的。代码如下:

01 <asp:datalist id="TopLevelList" runat="server" DataKeyField="Parent" ItemStyle-Cssclass="Normal" width="98%">
02 <ItemTemplate>
03 <asp:ImageButton id="btnSelect" ImageUrl='<%# NodeImage((int)DataBinder.Eval(Container.DataItem, "ChildCount")) %>' CommandName="select" runat="server" />
04 <asp:hyperlink Text='<%# DataBinder.Eval(Container.DataItem, "Title") %>' NavigateUrl='<%# FormatUrl((int)DataBinder.Eval(Container.DataItem, "ItemID")) %>' Target="_new" runat="server" ID="Hyperlink1" />,
05 from
06 <%# DataBinder.Eval(Container.DataItem,"CreatedByUser") %>
07 , posted
08 <%# DataBinder.Eval(Container.DataItem,"CreatedDate", "{0:g}") %>
09 </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.增加一个问题。不管增加的是顶层问题或是对某个问题的回复问题,都可以利用该存储过程。

01 CREATE PROCEDURE Portal_AddMessage
02 (
03 @ItemID int OUTPUT,
04 @Title nvarchar(100),
05 @Body nvarchar(3000),
06 @ParentID int,
07 @UserName nvarchar(100),
08 @ModuleID int
09 )
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返回。

SELECT @ItemID = @@Identity

2.获取顶层问题列表;

01 CREATE PROCEDURE Portal_GetTopLevelMessages
02 (
03 @ModuleID int
04 )
05 AS
06
07 SELECT
08 ItemID,
09 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的显示一个+号图标,没有回复数量的显示点图标。主要的是看明白这句:

11 (SELECT COUNT(*) -1 FROM Portal_Discussion Disc2 WHERE LEFT(Disc2.DisplayOrder,LEN(RTRIM(Disc.DisplayOrder))) = Disc.DisplayOrder) AS ChildCount,

3.获取某个指定问题。

该存储过程根据@ItemID参数,获取相应问题的信息。其中还包括了两个新字段信息。NextMessageID和PrevMessageID。

CREATE PROCEDURE Portal_GetSingleMessage
(
@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类似)的存储过程如下:

01
02 CREATE PROCEDURE Portal_GetNextMessageID
03 (
04 @ItemID int,
05 @NextID int OUTPUT
06 )
07 AS
08
09 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

 

其他细节,请看源代码吧。困了,睡觉了。