杂志网站的数据库设计与灵活的水平菜单

8
我需要使用asp.net web form(c#)和MS SQL Server作为后端数据库设计一个基于CMS的网站,用于周刊的发布。
示例数据
MagazinePages Table
PageID  PageName    LangID  PagePositionNo  PageURL     PageInheritance //PageID 
1           Home        1         10        Default.aspx    0
2           About Us    1         20        Page.aspx       0
3           PageOne     1         10        Page.aspx       2
4           PageTwo     1         20        Page.aspx       2
5           Media           1         30        Page.aspx       0
6           Video       1         10        Videos.aspx     5
8           News        1         40        News.aspx        0
9           Archive     1         50        #               0
10          Publication 1         60        Page.aspx       0
11          SpanishHome 2         10        Default.aspx        0
12          SpanisAboutUs 2       20        Page.aspx   0
------------------------------------------------------------------------------
Magazine
MagazineID  MagazineIssueCode   LangID  MagazineTitle   MagazineLiveIssue(CurrentIssue)
1       1           1   Mag Title       0
2       2           1   Mag Title       1
3       1           2   SpanisgMag Title    0
4       2           2   Mag Title       1
------------------------------------------------------------------------------

News Table
NewsID  NewsTitle   NewsCatID   MagazineID  Language    
1   News one    100     1       1
2   News two    100     1       1
3   news three  200     1       1
4   News four   300     1       1
5   News Five   100     2       1
6   News Six    300     2       1
7   News seven  200     2       2
------------------------------------------------------------------------------

上述方法存在问题,因为只有在所有记录都在MagazinePages中时才能创建子菜单,根据上面的例子,我只能为“关于我们”和“多媒体”创建子菜单,如何设计我的数据库,以便可以从不同的表中提取数据,例如通过类别(政治、文化……)从news表中提取数据,并从Magazine表中提取期号(101期、102期、103期等)。
我打算使用ASP菜单控件来实现这个功能,但可能不是非常灵活,因为我将拥有100多期杂志,我应该如何或者说我可以使用哪种多列菜单与asp.net一起使用。
我的数据库可能有很多缺陷,在这方面我将不胜感激地获得帮助,以便我可以将此数据库用于CMS系统。如果有任何问题,请随时提问。
补充:
如果我需要从MagazinePages表中提取所有菜单名称,那么这很容易,但是我被要求具有如示例所示的菜单结构。
这样做的问题是,我可以从MagazinePages表中生成AboutUsMultimedia的菜单,但是我在这个表中没有像政治、经济等这样的页面……101、102、103的问题,因为这些信息存储在不同的表中,例如新闻表中的News CategoryMagazine表中的期号。我更愿意改变我的表设计,并使其灵活地从单独的表中读取菜单信息,但我不确定如何做到这一点。
新闻表在此模式中未显示。
我的做法是创建了ps_Pages表来存储CMS页面,例如主页、关于我们、联系方式、媒体中心……
我将与杂志有关的页面(实际上是带有不同类别或标签的文章,例如文化、政治、体育、人物等)存储在art_Article表中。

在MagazinePages表中,是否有必要在所有内容前加上“Page”?例如:PageURL...还会有什么其他的URL吗?只有一个Page对吧?PageIsHomePage?这不能简单地写成IsHomePage吗? - Arran
我可以按照您的建议更改列名,URL列将包含处理程序文件(例如News.aspx、Video.aspx、Gallery.aspx等)。 - Learning
我的问题是,如果我需要从其他表格(例如杂志表)创建菜单,则数据库设计对于基于菜单的设计不够灵活。 - Learning
那么联接两个表怎么办?执行两个查询,获取所需数据,将其转换为最适合您的形式并填充到菜单中!不要期望找到一个适合您的内置查询。在编程时,不要根据遇到的问题改变设计!相反,改变您的查询并进行调整。 - Moslem Ben Dhaou
4个回答

4

为什么不使用单个页面处理内容呢?

只需调用Page.aspx?Issue=4&Page=4

然后在您的代码中,您就知道这是第4个问题,并且他们想要第4页,然后您可以在Page.aspx (.vb或.cs)中编写代码来翻译它,并决定布局。

例如:

Page.aspx?Issue=4&Style=Article&Content=5

因此,在代码中,您可以这样做:好的,这是问题4,获取问题4的数据库条目,好的,他们想要从问题4获取Content ID 5,然后以文章的形式放置。

这意味着您不必将额外的页面添加到数据库类型中,您只需根据需要添加内容,生成用于访问内容的URL的项只需显示所有内容即可。


我不明白你的意思,我该如何像杂志表格中显示的那样创建问题菜单。如果所有内容都在一个表格中,那很容易,但我还需要从其他表格中创建子菜单。 - Learning
创建问题菜单,您需要在数据库中选择所有标记为问题4内容的内容,然后遍历该列表。构建菜单列表,例如Page.aspx?Issue=4&Style = //页面变量样式和Content = //内容ID? - Ryan McDonough

3

我认为你需要重新评估你的数据库模式。 比如,我会创建一个名为“ParentMenuItems”的表。

这个表将包括所有顶部菜单项(首页、关于我们、问题等),以及子菜单所需的文本信息。 而后,你需要创建一个名为ChildMenu的表,该表与你的父菜单项有关联。

ParentMenuItems:
==============================================================
ID       | LinkText      | LangID     | Other Properties
==============================================================
1        | Home.aspx     | 1          | blah blah blah
2        | AboutUs.aspx  | 2          | blah blah blah

然后,您可以有另一个名为“ChildMenuItems”的表,如下所示:

ChildMenuItems:
===============================================================
ID   | LinkText       | LangID |  ParentID  | Other Properties...
===============================================================
1    | PageOne.aspx   | 1      | 2          | blah blah blah
2    | PageTwo.aspx   | 2      | 2          | blah blah blah

代码可能像这样工作:
SELECT * FROM ParentMenuItems - //SQL to get Items

然后,编写一些foreach代码来枚举SQL结果。
foreach(var ParentMenuItem in ParentMenuItems)
{ 
    //Get ParentMenuItem ID, run SQL select on child menu items, Example:
    //SELECT * from ChildMenuItems where ParentID = ID.FROM.PARENT.RESULT
    // Now you have all the child menu items, foreach those and add to repeater control
}

我希望这能有所帮助。 如果你有问题,请告诉我。
提示: Entity Framework可以轻松完成此任务。

我可以通过使用当前的表结构来实现相同的效果,因为我有一个名为“PageInheritance”的东西,它类似于你的“ParentID”。通过我的方法,我可以拥有任意级别的嵌套子菜单。我的问题是,我必须从“Magazine”表中创建类似子菜单的问题。 - Learning
感谢您的建议。我认为对于我的情况,最好从菜单中删除“问题”,并将其作为单独的下拉菜单,因为我有超过70个过去10-15年的问题。我将保留新闻菜单,因为我只需要创建3个项目,并将它们链接到正确的页面或编写查询以获取与政治、经济、文化相关的文章等。这将使我的生活变得轻松,因为我找不到适合我的情况的解决方案。谢谢大家。 - Learning

2
创建菜单表格
CREATE TABLE [dbo].[tblMenuMaster](
[MenuID] [int] IDENTITY(1,1) NOT NULL,
[MenuName] [varchar](100) NULL,
[DisplayOrder] [int] NULL,
PRIMARY KEY CLUSTERED 
(
[MenuID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

创建另一张表格用于子菜单
CREATE TABLE [dbo].[tblSubMenuMaster](
[SubMenuID] [int] IDENTITY(1,1) NOT NULL,
[MenuID] [int] NULL,
[SubMenuName] [varchar](100) NULL,
[MainMenuDisplayOrder] [int] NULL,
[DisplayOrder] [int] NULL,
[SubMenuUrl] [varchar](500) NULL,
[VisibleInMenu] [bit] NULL,
PRIMARY KEY CLUSTERED 
(
[SubMenuID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

现在进入主页面... HTML代码如下:
   <div class="menubar">
        <%--<ul class="tabs">--%>
        <asp:Literal ID="ltMenus" runat="server"></asp:Literal>
        <%--</ul>--%>
    </div>

代码后台是:

private void GenerateMenus()
{
    clsMenu obj = new clsMenu();
    System.Data.DataSet ds = new System.Data.DataSet();
    String PageName = "";
    PageName = Path.GetFileName(Page.AppRelativeVirtualPath);
    ds = obj.GetMenusByRole(GetRoleId(), PageName);

    StringBuilder sb = new StringBuilder("<ul class='tabs'>");

    foreach (System.Data.DataRow row in ds.Tables[0].Rows)
    {
        sb.Append(String.Format("<li class='{0}'><a rel='{1}' href='{1}' > {2} </a> ", Convert.ToString(row["css"]), ResolveUrl(Convert.ToString(row["PagePath"])), Convert.ToString(row["MenuName"])));
        //sb.Append(String.Format("<li '><a rel='{0}' href='{0}' > {1} </a> ", ResolveUrl(Convert.ToString(row["PagePath"])), Convert.ToString(row["MenuName"])));

        System.Data.DataTable t = CCMMUtility.GetFilterDataforIntColumn("MenuID", Convert.ToString(row["MenuID"]), ds.Tables[1]);
        if (t.Rows.Count > 0)
        {
            sb.Append("<ul>");

            for (int i = 0; i < t.Rows.Count; i++)
            {
                sb.Append(String.Format("<li><a href='{0}' class='dir' style='cursor: pointer;'>{1}</a></li>", ResolveUrl(Convert.ToString(t.Rows[i]["PagePath"])), Convert.ToString(t.Rows[i]["PageAliasName"])));
            }

            sb.Append("</ul>");
        }
        sb.Append("</li>");
    }

    sb.Append("</ul>");


    ltMenus.Text = sb.ToString();

}

根据角色ID动态调用菜单,需要使用存储过程,示例如下:

CREATE PROCEDURE [dbo].[proc_GetMenusByRole]
(
@RoleId int,  
@PageName varchar(100)
)
AS
SET NOCOUNT ON;
SELECT mm.MenuID, mm.MenuName,dbo.Extract_CssNameForMenuByMenuIDAndPageName(mm.MenuID, @PageName) as css
,dbo.proc_Extract_MenuPageByRoleIDAndMenuID(@RoleId, mm.MenuID)
as PagePath , mm.DisplayOrder   FROM tblMenuMaster mm WHERE mm.MenuID IN (SELECT s.MenuID from tblSiteRolePermissions p INNER JOIN
tblSitePages s ON p.fkSitePageId = s.pkSitePageId
WHERE (p.fkRoleId = @RoleId and p.ViewOnly=1))   
Union All   
select 0 as menuid ,'Change Password' as MenuName,  
case @pagename   
when 'ChangePassword.aspx' then 'active'  
else ''  
end  as css,'~/User/ChangePassword.aspx' as PagePath, 10000 as Displayorder  
ORDER BY DisplayOrder     
SELECT s.MenuID, s.pkSitePageId, s.PageAliasName, s.SitePageName,s.pagepath from tblSiteRolePermissions p 
INNER JOIN tblSitePages s ON p.fkSitePageId = s.pkSitePageId  WHERE (p.fkRoleId =@RoleId and p.ViewOnly=1) ORDER BY s.pkSitePageId  

//新的SP从这里开始

CREATE function [dbo].[Extract_CssNameForMenuByMenuIDAndPageName](@MenuID int, 
PageName varchar(100))
returns nvarchar(50)
as begin      
declare @result nvarchar(50) 
set @result = ''    
IF EXISTS (SELECT pkSitePageId FROM tblsitepages WHERE (MenuID = @MenuID) AND (UPPER(SitePageName) = @PageName)) 
 BEGIN    
  SET @result = 'active'    
 END    
return @result    
end  

// 另外一个常用的sp是

CREATE function [dbo].[proc_Extract_MenuPageByRoleIDAndMenuID]
(@RoleId int, @MenuID int)
returns nvarchar(500) 
as begin          
declare @result nvarchar(500)
SELECT top 1 @result = s.pagepath FROM tblSitePages AS s INNER JOIN tblSiteRolePermissions AS p ON s.pkSitePageId = p.fkSitePageId
WHERE (p.fkRoleId = @RoleId) AND (s.MenuID = @MenuID)  and p.ViewOnly=1
ORDER BY s.pkSitePageId 
return  @result
end  

这只是一种方法,您可以根据自己的需求进行修改......

proc_Extract_MenuPageByRoleIDAndMenuID 存储过程用于获取页面名称和其路径,

Extract_CssNameForMenuByMenuIDAndPageName 存储过程用于将 active 类设置为第一个 li,也就是第一个菜单项。 希望这能帮到您...... 这是可行的代码。


谢谢,Raman。我会研究一下的。它也能处理多级菜单吗?你所说的roleId是什么意思?我没有使用任何角色。 - Learning
是的,我已经告诉过你了。这是我项目中的一个工作示例。如果你想创建n级别,则只需创建另一个表并以与我们上面所做的相同方式进行关联即可。 - Ram Singh
我对这种方法感到担忧,因为我没有看到其他的做法,现在每当我们添加新的杂志问题或新的分类到新的部分时,我都必须创建一个新的菜单条目。感谢您的帮助。 - Learning
只需为其创建一个主控程序,从中您可以在这些表中进行插入/更新/删除操作... - Ram Singh

2
我不确定我理解您的需求,但我假设您想为整个系统创建菜单,而不是每个杂志都有一个菜单,是吗?
如果是这样,我认为您正在寻找错误的问题:问题不在于数据库模式,而在于您的菜单应该如何被自动填充N个不具有预定义标准的表。
我会创建一个菜单表 - 并使杂志页面和菜单成为两个独立的表:如果您正在创建类似CMS的系统,您可能会有一个页面,在该页面上,我可以完全独立于任何其他表格编辑类别、问题和菜单条目。
此外,我会创建一个“标签”系统,让所有新闻和问题都可以“标记”,而不是放在一个专属类别中。这将使得可能有一条新闻同时谈论政治和文化。然后,不是将用户引导到许多不同的pages.aspx,而是使用content.aspx?tag=politics。在创建标签时,我可以选择将此标签添加到菜单表中,“新闻”、“问题”等下面。类别和问题也是如此。
如果这不符合您的需求,您可以尝试这些选项 - 但除了特定的菜单条目表之外的所有其他解决方案都会在我的脑海中引发“警告,未来可能出现问题”的警报。
1- 创建一个菜单表,一个存储过程按照特定规则填充这个表,然后在像“类别”这样的表上触发,每当内容更改时重写菜单(听起来像是一个快速修复)。
2- 在magazinepages中添加“submenu_table”,“submenu_field”,“submenu_condition”等,并使用动态SQL选择数据(类似于Set @SQL='Select '+ submenu_field + ' from '+ submenu_table + ' where ' + submenu_condition; Exec(@SQL))。 (另一个快速修复,查询量大且可能很慢)
3- SQL 2008及以上版本还有层次结构ID字段([http://blogs.msdn.com/b/manisblog/archive/2007/08/17/sql-server-2008-hierarchyid.aspx])1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接