标签:product 需求 包括 查找 百度 编码 关联性 解决 获取
前言:“我们有一个订单列表,希望能够根据当前登陆的不同用户看到不同类型的订单数据”、“我们希望不同的用户能看到不同时间段的扫描报表数据”、“我们系统需要不同用户查看不同的生产报表列”。诸如此类,最近经常收到项目上面的客户提出的这种问题,即所谓的“数据权限”,经过开会讨论决定:在目前的开发框架上面搭建一套通用的数据权限功能。
本文原创地址:http://www.cnblogs.com/landeanfen/p/7760803.html
有了上面的引言,自然而然就引出了今天需要和大家讨论的话题——数据权限。作为开发人员,我们肯定知道,一般的系统都离不开权限模块,它是支撑整个系统运行的基础模块。而根据项目类型和需求的不同,权限模块的设计更是大相径庭。但不管怎么变,权限模块从大的方面来说,可以分为两种大的类型:功能权限 和 数据权限。
如果你动手去设计数据权限,当你去各大平台、百度、谷歌查找设计思路的时候,你会发现很难找到有用的资料,很多设计思路局限性非常大。其实原因很简单:数据权限的设计和他人系统关系紧密,一般不太容易拿到你的项目上面直接使用。
当然也有另外部分人说“数据权限并不能作为权限模块去设计”,比如博主看到这样一条评论
从一定程度上来说,这样理解也不为过,如果你觉得你的系统灵活性和配置性不需要那么高,把数据权限的规则在代码里面写死又何妨,博主所在公司的另外一个部门就是这么干的,除了编码量大一点,其实也没什么太大的问题!其实博主说这么多无非是想表达一个观点:没有绝对通用的数据权限设计思路,关键看合不合你用!当然本文的设计思路也是一样,不强制要求,不提通用。设计思路供你参考!
关于权限设计的杂谈就告一段落,凡是点到即止,再说了多了就说烂了。到目前为止,博主找到的一篇写得相对比较好的文章 通用权限管理设计 之 数据权限。这位博主是用sql去实现的,如果是把这个运用到EF里面的话,考虑到EF复杂的导航属性,会有一些问题。接下来说说博主这边想到的设计思路。
先说说博主所在项目的情况,和数据打交道的部分采用EntityFramework+Repository的传统模式去实现的,整个项目从上到下,就是一种典型的"伪DDD",什么是”伪DDD“?这里不做过多说明,使用过DDD的同仁应该很清楚。下面是设计思路流程图:
第一步:配置数据规则
第二步:页面使用数据规则
以上是一个大致的思路图,总的来说,要实现基于EF的数据权限设计,主要分为两大步骤
配置数据规则这里有三个大的方面:功能模块、数据资源、角色
这三者配置之后得到的一个结果就是某一类角色的某一个功能模块对哪个数据资源的数据规则是什么样的。比如有一条销售总监的数据规则,配置销售总监在订单模块里面订单这个实体的订单类型是销售订单的所有数据,这就是针对销售总监在订单模块的数据规则。可能最终数据库存储得到的数据类似这样:
RoleId | FunctionCode | Rules |
2 | OrderQuery |
{"rules":[{"field":"Order_Status","operate":"in","value":"[0,1,2]"},{"field":"Order_Type","op":"equal","value":"1"}],"logicoperate":"and"} |
3 | OrderQuery |
{"rules":[{"field":"Order_Status","operate":"in","value":"[0]"},{"field":"Product.Categary.Type","equal":"equal","value":"1"}],"logicoperate":"and"} |
5 | Product |
{"groups":[ {"rules":[{"field":"Order_Status","operate":"in","value":"[0,5,10]"},{"field":"Order_Type","op":"equal","value":"1 "}],"logicoperate":"and"}, {"rules":[{"field":"LineName","operate":"equal","value":"fenzhuangxian"}]} ],"logicoperate":"or"} |
需要特别说明的是:由于EF有导航属性,这里的Rules在保存的时候如果遇到导航属性,我们的字段值需要这样保存——Product.Categary.Type。因为在我们转换成为lambda表达式的时候导航属性会是这样写:x=>x.Product.Categary.Type==1。这个我们在后面使用这个规则的时候加以说明。
有了上面的数据规则,接下来就是我们在取数据的时候如何使用了,这里有一点需要说明的是:我们这里需要传两个参数,一个是模块的名称,比如上面的OrderQuery、Product等;第二个是当前用户的角色id,这个可以通过当前登陆用户的id获取到角色。
要使用数据规则,之前博主分享过两篇关于动态Lambda的文章,现在派上用场了。只不过原来只是一些基础类型转lambda,现在涉及到了导航属性,不知道是否可行。博主查阅了一些资料,最终找到了解决方案。
//遍历得到属性(包括遍历导航属性) public Expression GetProperty(Expression source, ParameterExpression para, string Name) { string[] propertys = Name.Split(‘.‘); if (source == null) { source = Expression.Property(para, typeof(Entity).GetProperty(propertys.First())); } else { source = Expression.Property(source, propertys.First()); } foreach (var item in propertys.Skip(1)) { source = GetProperty(source, para, item); } return source; }
然后测试如下
var oLamadaExtention = new LambdaExpression<Order>(); var left = oLamadaExtention.GetProperty(null, Expression.Parameter(typeof(Order), "x"), "Product.Categary.Type"); var value = Expression.Constant("1", left.Type); //动态转换类型 var right = Expression.Constant(value, left.Type); Expression expRes = Expression.Equal(left, right);
测试得到的查询lambda结果为x=>x.Product.Categary.Type=="1",测试成功!
对于配置数据规则的时候还有一点比较麻烦的是,如果如何知道哪个功能模块使用哪些实体?不可能直接让用户去写Product.Categary.Type这些复杂的功能吧,如果是这样,谈何体验。那么只有使用另外一种解决思路了——反射EF实体。
反射EF实体的时候如果是导航属性,还得继续反射导航属性的实体,这样一层一层反射下去,最终确实是可以得到形如Product.Categary.Type这个的结构体,但界面如何展现还有待思考。比如思路如下:
以上只是一个设计思路,理论上来说是可以实现的,如有不足,欢迎斧正,谢谢。如果思路没有问题,后续博主会抽时间将这种设计的实现过程展现出来供大家参考,欢迎关注。其中的难点有两个:
1、逐级反射EF的导航属性,以及这个过程如何展现。是通过特性标记,还是开发人员配置;
2、动态Expression在构造Lambda的时候和配置数据的兼容性问题,比如数据类型的兼容性有点难控制。
本文原创出处:http://www.cnblogs.com/landeanfen/
欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利
数据权限设计——基于EntityFramework的数据权限设计方案:一种设计思路
标签:product 需求 包括 查找 百度 编码 关联性 解决 获取
原文地址:http://www.cnblogs.com/landeanfen/p/7760803.html