上一文中讲了带编号(或说是路径)的树状目录结构基本操作。谢谢有朋友提醒SQL Server2008以上版本可以采用HierarchyId类型字段,也很希望能看到阐述这方面最佳实践的文章。

  理论联系实践,再提高理论认识,这是我一直追求的一个良性循环。本文将目录结构在实践中的一个应用,其实和结构存储方式倒没直接关系。

  先请大家看个截图,这是一个部门选择菜单控件。

image

  这是一个纯CSS实现的多层级下拉菜单,相对来说在网页上较少见。当点击选择一个部门时,旁边的文本框会显示该部门的各层级名称,并将该部门编号记录到一个隐藏域中,用于提交查询。

  这个控件和遍历目录下的文件一样,用递归方式写入HTML代码,应该算基本的编程技术了。

public partial class SectionMenu : System.Web.UI.UserControl
{
    protected StringBuilder HtmlBuilder = new StringBuilder();

    protected void Page_Load(object sender, EventArgs e)
    {
        WriteSectionMenu(null);
    }

    public void WriteSectionMenu(string parentID)
    {
        var topSections = SectionHelper.GetSubSections(parentID, true); //读取一级子部门

        if (topSections.Count < 1) return;
        HtmlBuilder.Append("<ul>");

        foreach (var item in topSections)
        {
            HtmlBuilder.AppendFormat("<li><a href=\"{0}\">{1}</a>", item.SectionNo, item.Name);

            WriteSectionMenu(item.SectionNo);
            HtmlBuilder.Append("</li>");
        }

        HtmlBuilder.Append("</ul>");
    }
}

  菜单前端实现参考示例(单html文件):下载

  这种下拉菜单方便之处一目了然,缺点是导致页面体积显著增大,像示例那种,一两百个部门,只增加了十几KB还好,更多的话就令人难以接受了。解决的方案我认为只有一种—缓存。

  缓存是B/S开发中的最深的一门学问,没有之一。一个男人,如果能在老妈和老婆中间左右逢源,便是一个不算失败的男人;如果男人还能照顾到子女的平衡,必须是一个优秀的男人。同样,在Web开发中,能灵活运用数据缓存和输出缓存的,便是一个不错的程序员;若能考虑充分利用客户端缓存,应该就是个杰出的程序员;而如果他能设计全局缓存和节点缓存的话,我想那是大师级人物吧。

  我想提供一下Server端的数据缓存和输出缓存,以及Client的浏览器缓存方案,可惜我还没实现完整,放在下文再讲吧。有朋友似乎对我上文中的理论不太认可,我提供两段Linq To SQL代码,实现的功能相同,都是查找某部门员工的请假记录(Leave表),但存储结构不同。

  记录父级编号存储方式:

var sectionNo = "00";   //部门编号

//所有层级子部门键人集合
var subSections = SectionHelper.GetSubSections(sectionNo, false).Select(s=>s.Id).ToList(); 

var result = from l in db.Leaves
             join p in db.PersonInfoes on l.Alias equals p.Alias
             where subSections.Contains(p.SectionId)
             select l;
             
query = (result as ObjectQuery).ToTraceString();

  生成的SQL语句:

SELECT [Extent1].[ID] AS [ID], [Extent1].[Type] AS [Type], [Extent1].[BeginTime] AS [BeginTime], [Extent1].[EndTime] AS [EndTime], [Extent1].[Alias] AS [Alias], [Extent1].[AssistantID] AS [AssistantID], [Extent1].[PostTime] AS [PostTime] FROM [Affair].[Leave] AS [Extent1] INNER JOIN [Firm].[PersonInfo] AS [Extent2] ON [Extent1].[Alias] = [Extent2].[Alias] WHERE [Extent2].[SectionId] IN (1,2,5,7,13,24,……,N)

  记录路径编号存储方式:

var sectionNo = "00";   //部门编号
var result = from l in db.Leaves
             join p in db.PersonInfoes on l.Alias equals p.Alias
             where p.SectionNo.IndexOf(sectionNo) == 0
             select l;

Console.WriteLine((result as ObjectQuery).ToTraceString()); 

  生成的SQL语句是:

SELECT [Extent1].[ID] AS [ID], [Extent1].[Type] AS [Type], [Extent1].[BeginTime] AS [BeginTime], [Extent1].[EndTime] AS [EndTime], [Extent1].[Alias] AS [Alias], [Extent1].[AssistantID] AS [AssistantID], [Extent1].[PostTime] AS [PostTime] FROM [Affair].[Leave] AS [Extent1] INNER JOIN [Firm].[PersonInfo] AS [Extent2] ON [Extent1].[Alias] = [Extent2].[Alias] WHERE 0 = (( CAST(CHARINDEX(@p__linq__0, [Extent2].[SectionNo]) AS int)) - 1)

  孰优孰劣,大家可以比较下生成的SQL语句,应该会有一些概念。

posted on 2011-04-14 14:16  小城故事  阅读(3397)  评论(1编辑  收藏  举报