mvc mysql用户管理_一步一步Asp.Net MVC系列_权限管理数据库与ViewModel篇

在上一篇中我们讲了大致怎么搭建一个项目,以及一个项目的基本构架,这一次,我们讲解基本的权限管理思路.

说道权限管理,相信大家都不太陌生,这个东西几乎什么系统都会涉及到,因此,抽出时间去思考,去研究复用的模块,架构,就是一个非常好的提升水平的方式,特别是对于我们这些学生来说,没有太多经验,更需要去研究这些东西来更多的掌握实战方面的技巧.数据库设计是一个几乎人人都要面对的一个话题,我就来讲,我的数据库设计.当然只是个人见解,博客园的很多人都设计的各种各样的权限,各种各样的思路,但是可能大多数设计的比较复杂,我只是从一个菜鸟的角度讲解,我能够理解的权限管理.

首先是数据库设计:

在此期间,我们贴上powerdesinger设计图

c8acd48d18987c25c146f975800dcc25.png

大致就是7张表:

tbModule模块表

tbModulePermission模块权限表

tbPermission权限表

tbRole角色表

tbRolePermission角色权限表

tbUserRole用户角色表

tbUser用户表

首先要注意的就是命名:

可以看到,我的数据库命名风格是非常统一的,呵呵,这个可能是习惯,tb代表table,如果是视图我就在前面加上v,后面全部是Pascal风格的命名,字母大写的方式.

一个清晰的命名可以让我们很容易看清细节,起一个好名字非常重要.

这也告诉我们,我们平时注意细节,命名,代码规范统一,让我们节省大量的时间.

在博客园看到很多权限设计思路,

我觉得上面这个可能是比较清晰的,呵呵,参考了好多人的设计,

权限设计要设计那些呢?

首先,一个模块对应多种权限,增删改查,导入导出等等等等,这里权限在mvc里面可以控制到每一个请求,每一个ajax响应,在mvc里面有一个很好的对应关系,一个模块对应就是一个控制器,一个action就是一个权限(当然这种可能太细了,但是我们可以先这么看),这样我们就需要在tbModule和tbPermission中间加一个tbModulePermission表了,因为不同角色的同一模块的权限可能都是不一样的,所以这里要加入一个tbModulePermission表.

其次,角色的设计,角色拥有模块权限,每个用户对应多种角色,这个用户可能即是普通管理员,又是其他后台编辑,等等,权限当然是不一样的.

这样一个思路就很明显了,角色直接拥有各种模块的功能权限,而用户可以扮演各种角色.

至于字段的设计,我看了很多人的设计,很多细节的字段,比如IsDeleted,CreateUserID,CreateDate,ModifyUserID,ModifyDate,Description这些几乎大部分表都是需要的,多加一些表,更是让设计更灵活.

数据库的设计ID用GUID的习惯,我还没有养成,可能现在没遇到麻烦吧,也是没有真正的工作经验,呵呵,别人都说应该主键用GUID,呵呵!

我看到很多人直接设计Menu和Button来表示tbModule和tbPermission,其实感觉从命名来讲,页面任何一个ajax请求都可以算是一种功能权限,可能不是Button,如果用Permission表示可能更好一点,至于Menu,我认为用Module来命名可能更好,在tbModule里面有一个IsMenu字段来表示是否是菜单,里面有一个Controller字段,其实大致对应一个Controller控制器,每一个模块在mvc中以控制器的方式展示,我们在设计UI菜单的时候也可以通过反射的方式来获取Controller,对于菜单的设计更友好,不需要自己去配置URL,更自由.当然,在Permission表里面有PermissionAction和PermissionController字段表示,每一个动作请求和对应的控制器.

1e59cb2ef3ff4430044864bea2762d4a.png

对于菜单的添加我们可以做到非常轻松,点点点就能够配置好,以前需要在数据库输入的一些预定义的数据!

数据设计好了,这种数据库可能我感觉比较清晰.呵呵!

接下来,我们继续谈关于设计的问题,上次基本上已经讲完关于数据访问层的设计,这次讲解,关于ViewModel的设计和公共类库的设计,

ViewModel呵呵,我用它来干什么,我用它来做UI层和业务逻辑层交互的实体对象.

可能大家觉得EF已经帮我们设计了数据库的实体对象,为什么还要另外一个单独的层来定义交互的实体对象?

866bc4a3c4bf1e656d197ee3cd9ef39e.png

我把这个ViewModel就是和界面交互的实体对象,在EF基本上是根据数据库直接生成的实体,所以命名字段都是跟数据库紧密结合在一起的,往往我们给UI层展示的时候,要做大量的处理,而UI交互,经常性我们用的后台框架都要求提供指定格式的json数据,我们以前甚至这么干过,从数据访问层直接select DeptID as id,name as title from deptment……………这种代码简直太坑爹了,而几乎数据库的东西和界面都关联到一起了,如果我们修改个字段,界面改点东西,两头都要动,所以,这种模式太痛苦了.

因此,我专门独立了一层,用来做UI和业务逻辑层的交互使用,这样ViewModel与UI更友好,用例子说说吧!

数据库的Permission在界面中大量显示为Button,我们就可以直接定义一个ViewModelButton类,来做UI和业务逻辑的交互,

如果对于普通字段的添加,我相信在这个代码中,你的改动非常少,几乎只是EF模型重新生成,ViewModel,和html界面传参的修改,我们的目标是,让修改更简单,业务的变动,我们仍然能够清晰的把握细节.

1:/* 作者: tianzh

2:* 创建时间: 2012/7/26 15:52:19

3:*

4:*/

5:using System;

6:using System.Collections.Generic;

7:using System.Linq;

8:using System.Text;

9:using System.Web;

10:using TZHSWEET.Entity;

11:

12:namespace TZHSWEET.ViewModel

13:{

14: public class ViewModelButton

15: {

16: #region - 属性 -

17:

18: public int ID { get; set; }

19:

20: public string Action { get; set; }

21:

22: public string Name { get; set; }

23:

24: public bool IsVisible { get; set; }

25:

26: public string Script { get; set; }

27:

28: public string Icon { get; set; }

29:

30: public string Controller { get; set; }

31:

32: public string Description { get; set; }

33:

34: #endregion

35:

36: #region - 构造函数 -

37:

38: public ViewModelButton()

39: {

40:

41: }

42:

43: public ViewModelButton(HttpContextBase context)

44: {

45: ID = Convert.ToInt32(context.Request["ID"]);

46: Action = context.Request["Action"];

47: Name = context.Request["Name"];

48: IsVisible = context.Request["IsVisible"] == "1";

49: Script = context.Request["Script"];

50: Icon = context.Request["Icon"];

51: this.Controller = context.Request["Controller"];

52:

53:

54: }

55:

56: #endregion

57:

58: #region - 方法 -

59: #region ToEntity

60: public static tbPermission ToEntity(ViewModelButton permission)

61: {

62: tbPermission item = new tbPermission();

63: item.PermissionID = permission.ID;

64: item.PermissionName = permission.Name;

65: item.PermissionAction = permission.Action;

66: item.Script = permission.Script;

67: item.Icon = permission.Icon;

68: item.IsVisible = permission.IsVisible;

69: item.PermissionController = permission.Controller;

70: item.Description = permission.Description;

71: return item;

72: }

73:

74: #endregion

75: #region ToViewModel

76: ///

77: /// 转化为ViewModel

78: ///

79: ///

80: ///

81: public static ViewModelButton ToViewModel(tbPermission permission)

82: {

83: ViewModelButton item = new ViewModelButton();

84: item.ID = permission.PermissionID;

85: item.Name = permission.PermissionName;

86: item.Action = permission.PermissionAction;

87: item.IsVisible = permission.IsVisible.Value;

88: item.Icon = permission.Icon;

89: item.Script = permission.Script;

90: item.Controller = permission.PermissionController;

91: item.Description = permission.Description;

92: return item;

93: }

94:

95: ///

96: /// 转化为List集合

97: ///

98: ///

99: ///

100: public static IEnumerable ToListViewModel(IEnumerable list)

101: {

102: var listModel = new List();

103: foreach (tbPermission item in list)

104: {

105: listModel.Add(ViewModelButton.ToViewModel(item));

106: }

107: return listModel;

108: }

109: #endregion

110:

111: #endregion

112:

113:

114: }

115:}

一般情况下,我们只需要定义实体,和一个构造函数,这个构造函数,大家可能觉得为什么要传递HttpContext?

原因就是,封装Controller中的请求,把Controller中表单的数据请求单独用ViewModel处理,看看关于Controller的一个函数处理

22c720c6f1015bda6c858d61b0048e87.png

我们把关于请求的数据从Controller中分离处理啊,Controller就相当简洁了,很清晰的看到具体的思路,而我们的业务逻辑层,就可以相当简洁的调用

5567f0ce7f6cf6ea7e2a50249a164382.png

而这个T是什么呢?T就是tbPermission实体这个东西了,是不是发现页面非常清晰,简洁?

而我们经常通用的几种UI格式,LigerUI Grid格式请求,Select格式请求,GridTree格式请求,Tree格式请求,都是ViewModel中的封装,

66046db99b7cc091770e00a8f14f6ed5.png

GridRequest请求的ViewModel:

1: /* 作者: tianzh

2:* 创建时间: 2012/7/19 18:16:08

3:*

4:*/

5: /* 作者: tianzh

6:* 创建时间: 2012/7/16 15:31:37

7:*

8:*/

9:

10:using System;

11:using System.Collections.Generic;

12:using System.Linq;

13:using System.Text;

14:using System.Web;

15:namespace TZHSWEET.ViewModel

16:{

17: public class LigerUIGridRequest

18: {

19:

20: private string sortOrder;

21:

22: private int pageSize;

23: ///

24: /// 字段查看视图(暂时没做到)

25: ///

26: public string View

27: {

28: get;

29: set;

30: }

31: ///

32: /// 排序字段名称

33: ///

34: public string SortName { get; set; }

35: ///

36: /// 排序规则

37: ///

38: public string SortOrder

39: {

40: get

41: {

42: if (this.sortOrder == "desc")

43: return this.sortOrder;

44: else

45: return "asc";

46: }

47:

48: set

49: {

50: this.sortOrder = value;

51: }

52: }

53: private int pageNumber;

54: ///

55: /// 页号

56: ///

57: public int PageNumber

58: {

59: get

60: {

61: if (this.pageNumber <= 0)

62: return 1;

63: else

64: return pageNumber;

65: }

66: set

67: {

68: if (value <= 0)

69: pageNumber = 1;

70: else

71: pageNumber = value;

72: }

73: }

74: ///

75: /// 每页的多少条数据

76: ///

77: public int PageSize

78: {

79: get

80: {

81: return this.pageSize;

82: }

83: set

84: {

85: this.pageSize= (value==0?10:value);

86: }

87: }

88: ///

89: /// 查询条件

90: ///

91: public string Where { get; set; }

92: ///

93: /// 初始化读取信息

94: ///

95: ///

96: public LigerUIGridRequest(HttpContextBase context)

97: {

98: this.View = context.Request["view"];

99: this.SortName= context.Request["sortname"];

100: this.SortOrder = context.Request["sortorder"]=="desc"?"desc":"asc";

101: this.PageNumber =Convert.ToInt32(context.Request["page"]);

102: this.PageSize =Convert.ToInt32(context.Request["pagesize"]);

103: this.Where = context.Request["where"];

104:

105: }

106: }

107:}

在这里我们可以做数据的校验工作,这样我们的控制器就非常非常的简洁了,

在业务逻辑层,我们可以通过Request的ViewModel模型,进行数据处理,

9d6414ce006edc56be574b22b4c8e764.png

可以看到我们仅仅通过数据的封装就让我们的UI和业务逻辑更简洁,更丰富,

当然这个业务逻辑层写的很有问题..............就是业务逻辑层竟然直接返回json格式,这个大大的不好,因为我们的业务逻辑层尽可能的跟数据的展示格式没关系,应该直接返回数据,集合就直接返回IEnumerable接口,而数据如何展示应该在控制器中控制.

一个好一点的方法设计就是这样子的,

f722a1eb28780e01cb574bac71273a31.png

可以看到我们的工作大部分就是让不同的类减压,尽量更简单,单一指责,如果一个类的职责过多就更难维护,特别是控制器,很容易出现大量的代码,

就像我们的这个

51a19a76aa0629d8cf805e9fd2add461.png

让代码更精简,这样我们才能更清晰,本来我们返回的Grid数据需要json化,我写了一个扩展方法,返回json,而且格式针对UI更友好,配合前台的封装的ajax,让消息的提示更友好,

ab6151310beec7748e2c480f927fd637.png

da9022e25ab428dbd47be19d0cace453.png

前台利用LigerUI中的封装,

85e78e164391d491c09309e0d7c165a3.png

60e9f3c6ac3395cb74ca9a81e232af81.png

总结:可以看到我们每一部细节的改进,都能让我们代码更简洁,而且让我们的思路更清晰,清晰,才是我们的追求,清晰的代码是我们的追求!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值