导航、权限管理及 Linq 应用

本文介绍了一个基于ASP.NET的广告系统后台管理模块的设计思路,重点在于使用List存储导航菜单并根据权限动态生成HTML代码,实现了灵活的页面级访问控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  最近做一个广告系统,后台管理部分使用 ASP.NET,使用了 VS2008 + .Net 3.5,还是使用我写的 DbEntry 做数据库接口,页面部分大部分使用 ASP.NET Ajax 的 UpdatePanel 来进行更新,效果很不错,而且,速度上也感觉比普通的非 Ajax 页面快。

  而对于权限部分,使用页面级访问控制,读取 Web.Config 的方式,导航使用 html 直接放在母板页中的方式,后来觉得这样,每增加一个页面或对页面改名,就需要修改两个地方,不是一个很好的解决方案。

  以前,在做互联星空的一个项目时,曾经设计了使用 XML 做配置,导入 TreeView 做导航的方式,目前虽然不是使用 TreeView 做导航,应该也一样是适用的。不过,这一次,不想使用 XML,而是考虑直接在程序中使用 List 或 Directory 来表示这个数据结构。

  试着写了一下这个结构,发现,还是使用类似数据库的结构比较简单。于是定义 Category 和 Link 类,另外定义 Navigation 类,增加两个静态字段 Categories 和 Links:

public class Category
{
private static int IdSeed = 1;

public int Id { get; set; }
public string Name { get; set; }

public Category()
{
Id = IdSeed++;
}
}

public class Link
{
private static int IdSeed = 1;

public int Id { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public int Permission { get; set; }
public bool Hide { get; set; }
public int Category_Id { get; set; }

public Link()
{
Id = IdSeed++;
}
}

public static class Navigation
{
public static readonly List<Category> Categories;
public static readonly List<Link> Links;
}

  然后,在静态构造函数中初始化数据:

static Navigation()
{
Categories = new List<Category>()
{
new Category(){ Id = 1, Name = "设备管理" },
new Category(){ Id = 2, Name = "内容管理" },
new Category(){ Id = 3, Name = "计划" },
new Category(){ Id = 4, Name = "系统" },
};

int FullPermission = (int)(SysUserRole.管理员 | SysUserRole.编辑人员 | SysUserRole.审核人员);

Links = new List<Link>()
{
new Link(){ Url = "Default", Permission = FullPermission, Hide = true },
// 设备管理
new Link(){ Name = "新建设备", Url = "PlayerEdit", Permission = FullPermission, Category_Id = 1 },
new Link(){ Name = "设备列表", Url = "PlayerList", Permission = FullPermission, Category_Id = 1 },
new Link(){ Url = "PlayerManager", Permission = FullPermission, Hide = true, Category_Id = 1 },
new Link(){ Category_Id = 1 },
new Link(){ Name = "新建分组", Url = "PlayerGroupEdit", Permission = FullPermission, Category_Id = 1 },
new Link(){ Name = "分组列表", Url = "PlayerGroupList", Permission = FullPermission, Category_Id = 1 },
// 内容管理
......
// 计划
......
// 系统
new Link(){ Name = "新建帐户", Url = "SysUserEdit", Permission = FullPermission, Category_Id = 4 },
new Link(){ Name = "帐户列表", Url = "SysUserList", Permission = FullPermission, Category_Id = 4 },
new Link(){ Category_Id = 4 },
new Link(){ Name = "新建客户", Url = "CustomerEdit", Permission = FullPermission, Category_Id = 4 },
new Link(){ Name = "客户列表", Url = "CustomerList", Permission = FullPermission, Category_Id = 4 },
};
}

  其中,空的 Link 表示显示一个 hr 标签,用来分组,Url 是去掉“.aspx”之后的名字,Permission 用来对页面可访问性进行授权,这种方式,对于 int32 而言,可以提供 32 种权限,应该是足够了,当然,各种权限应该按 2 的幂的方式递增,如 1、2、4、8。这样,在判断是否有权限的时候,只要用“(Permission & p) == p”来判断就可以了。

  导航的 html 改在 Navigation 类中生成,然后填入母板页中的方式,使用 DbEntry 中的 HtmlBuilder 来生成 html 片段:

public static string BuildNavigator(int Permission)
{
HtmlBuilder hb = HtmlBuilder.New.div.id("accmenu").enter();
foreach (var c in Categories)
{
int count = 0;
var b = HtmlBuilder.New.tab.div.enter();
b.tab.tab.div.Class("acctitle").text(c.Name).end.enter();
b.tab.tab.div.enter();
var list = Links.Where(p => p.Category_Id == c.Id).ToList();
foreach (var n in list)
{
if (string.IsNullOrEmpty(n.Url))
{
b.include("\t\t\t<hr />\r\n");
}
else
{
if (!n.Hide && (n.Permission & Permission) == Permission)
{
count++;
b.include("\t\t\t<img src=\"images/h2.gif\" align=\"absmiddle\" /> ");
b.a(n.Url + ".aspx").text(n.Name).end.br.enter();
}
}
}
b.tab.tab.end.enter().tab.end.enter();
if (count > 0)
{
hb.include(b);
}
}
hb.end.enter();
return hb.ToString();
}

  上面的代码,会生成类似以下的 html :

<div id="accmenu">
<div>
<div class="acctitle">设备管理</div>
<div>
<img src="images/h2.gif" align="absmiddle" /> <a href="PlayerEdit.aspx">新建设备</a><br />
<img src="images/h2.gif" align="absmiddle" /> <a href="PlayerList.aspx">设备列表</a><br />
<hr />
<img src="images/h2.gif" align="absmiddle" /> <a href="PlayerGroupEdit.aspx">新建分组</a><br />
<img src="images/h2.gif" align="absmiddle" /> <a href="PlayerGroupList.aspx">分组列表</a><br />
</div>
</div>
<div>
......
</div>
<div>
......
</div>
<div>
<div class="acctitle">系统</div>
<div>
<img src="images/h2.gif" align="absmiddle" /> <a href="SysUserEdit.aspx">新建帐户</a><br />
<img src="images/h2.gif" align="absmiddle" /> <a href="SysUserList.aspx">帐户列表</a><br />
<hr />
<img src="images/h2.gif" align="absmiddle" /> <a href="CustomerEdit.aspx">新建客户</a><br />
<img src="images/h2.gif" align="absmiddle" /> <a href="CustomerList.aspx">客户列表</a><br />
</div>
</div>
</div>

  而,它的显示,使用的是 jQuery 的插件 [url=http://docs.jquery.com/UI/Accordion]jQuery Accordion[/url] ,按照自己想要风格配置 css,然后在母板页中加入:

jQuery().ready(function(){
var w = $('#accmenu').accordion({
header: 'div.acctitle',
animated: false
});
w.activate(CategoryIndex);
});

  之所以要关闭动画效果,是为了根据当前页所在分类,自动激活相应的面板,自动激活的过程如果有动画,显得很奇怪,不过,还没有找到这个 jQuery 插件相关设置初始面板的方法......

  Navigation 类提供取得指定 Url 权限的功能,使用基本的 Linq 语法:

public static int GetPermission(string Url)
{
var item = Links.Where(p => p.Url == Url).ToList();
if (item.Count > 0)
{
return item[0].Permission;
}
return 0;
}

  因为,在生成 html 的时候,考虑了如果按照相应的权限,一个分类下没有任何项目,则不显示这个分类,所以,取 Index 要复杂一些,要根据相应的权限进行分组,所以相应的 Linq 语句也复杂一些,使用了 group by:

public static int GetIndex(string Url, int Permission)
{
int id = FindCategoryId(Url);
var item = from p in Links where (p.Permission & Permission) == Permission && p.Category_Id != 0
group p by p.Category_Id into g select new { Category_Id = g.Key };
var i = item.ToList().FindIndex(p => p.Category_Id == id);
return i < 0 ? 0 : i;
}

public static int FindCategoryId(string Url)
{
var item = Links.Where(p => p.Url == Url).ToList();
if (item.Count > 0)
{
return item[0].Category_Id;
}
return 0;
}

  虽然我现在使用的是内存里的数据,不过,因为格式是很标准的数据库格式,所以,要把这个配置项放入数据库表里,或者序列化成 XML,也都是非常方便的 —— 虽然我认为这个必要性不高。

  从实现来看,这个方法的速度应该不会很快,不过,因为数据量小,而且对于页面来说,这些在内存里做的手脚只能算小Case,所以没有明显感觉速度上有任何差异。

  不过,目前对于这个方案,还有一些不满意,比如,Hide 参数考虑改成和 Permission 相似,则可以控制每一项在不同权限下的显示,比单纯的全局 Hide 要灵活得多。再比如,目前没有判断是否会出现两条分割线等等。

  另外一种实现方案是,把数据的定义放在每一个页面里,这样的话,虽然设置分散到了每一个页面,但是却更符合实际情况,而且,页面 Url 也可以通过反射得到,删除页面或者页面改名都更简单,也许是更好的解决方案吧。

  其实,我感觉很好的是,这个程序的页面风格、css 以及一些图片什么的,都是我做的,而且我感觉还挺漂亮的 :D ,来个运行截图:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值