數據權限設計——基於EntityFramework的數據權限設計方案:一種設計思路

 前言:「咱們有一個訂單列表,但願可以根據當前登錄的不一樣用戶看到不一樣類型的訂單數據」、「咱們但願不一樣的用戶能看到不一樣時間段的掃描報表數據」、「咱們系統須要不一樣用戶查看不一樣的生產報表列」。諸如此類,最近常常收到項目上面的客戶提出的這種問題,即所謂的「數據權限」,通過開會討論決定:在目前的開發框架上面搭建一套通用的數據權限功能。html

本文原創地址:http://www.cnblogs.com/landeanfen/p/7760803.htmlsql

1、大話權限模塊

有了上面的引言,天然而然就引出了今天須要和你們討論的話題——數據權限。做爲開發人員,咱們確定知道,通常的系統都離不開權限模塊,它是支撐整個系統運行的基礎模塊。而根據項目類型和需求的不一樣,權限模塊的設計更是截然不同。但無論怎麼變,權限模塊從大的方面來講,能夠分爲兩種大的類型:功能權限數據權限數據庫

  • 功能權限:主要控制不一樣的資源主體(用戶、角色、組織等)有操做不一樣的資源的權限。好比常見的不一樣的角色能訪問不一樣的頁面(菜單權限),以及具備操做同一頁面的不一樣功能(按鈕權限)等等,都數據功能權限的範疇,這種設計相對比較簡單,也比較爲大多數系統所通用。固然網上資料、設計思路也能夠找到不少。
  • 數據權限:主要控制不一樣的資源主體(用戶、角色、組織等)有查看不一樣的數據信息的權限,通常來講,數據權限又分爲數據行權限數據列權限,經過字面意思不難理解這二者的區別,好比上文「咱們有一個訂單列表,但願可以根據當前登錄的不一樣用戶看到不一樣類型的訂單數據」 這就是一個典型的數據行權限,而「咱們系統須要不一樣用戶查看不一樣的生產報表列」這就是數據列權限的範疇。因爲數據權限和系統的業務邏輯關係很是密切,因此不一樣的系統設計差別性會很是大。從另外一方面來講,因爲數據權限和業務邏輯關聯性很是強,若是系統的業務邏輯很是複雜,數據權限設計起來也會相對複雜,因此關於數據權限的設計一直沒有一種相對通用和使用簡單的設計方案。

若是你動手去設計數據權限,當你去各大平臺、百度、谷歌查找設計思路的時候,你會發現很難找到有用的資料,不少設計思路侷限性很是大。其實緣由很簡單:數據權限的設計和他人系統關係緊密,通常不太容易拿到你的項目上面直接使用。框架

固然也有另外部分人說「數據權限並不能做爲權限模塊去設計」,好比博主看到這樣一條評論post

從必定程度上來講,這樣理解也不爲過,若是你以爲你的系統靈活性和配置性不須要那麼高,把數據權限的規則在代碼裏面寫死又何妨,博主所在公司的另一個部門就是這麼幹的,除了編碼量大一點,其實也沒什麼太大的問題!其實博主說這麼多無非是想表達一個觀點:沒有絕對通用的數據權限設計思路,關鍵看合不合你用!固然本文的設計思路也是同樣,不強制要求,不提通用。設計思路供你參考!測試

2、一種設計思路

關於權限設計的雜談就告一段落,凡是點到即止,再說了多了就說爛了。到目前爲止,博主找到的一篇寫得相對比較好的文章 通用權限管理設計 之 數據權限。這位博主是用sql去實現的,若是是把這個運用到EF裏面的話,考慮到EF複雜的導航屬性,會有一些問題。接下來講說博主這邊想到的設計思路。編碼

先說說博主所在項目的狀況,和數據打交道的部分採用EntityFramework+Repository的傳統模式去實現的,整個項目從上到下,就是一種典型的"僞DDD",什麼是」僞DDD「?這裏不作過多說明,使用過DDD的同仁應該很清楚。下面是設計思路流程圖:url

第一步:配置數據規則
spa

第二步:頁面使用數據規則設計

 

 以上是一個大體的思路圖,總的來講,要實現基於EF的數據權限設計,主要分爲兩大步驟

一、配置數據規則

配置數據規則這裏有三個大的方面:功能模塊數據資源角色

  • 功能模塊:爲何這裏要加上功能模塊的約束?是由於博主以爲咱們某一個頁面在查詢數據的時候,會有一個查詢的範圍,好比訂單查詢頁面確定只能查詢和訂單有關聯的實體功能,而不可能查詢和它沒有任何關聯的業務。加這個約束更大的意義在於咱們動態的構造Lambda去查詢實體的時候不會產生」找不到相關聯的實體「之類的錯誤
  • 數據資源:具體對哪一種數據資源作數據權限,好比訂單的狀態不等於取消狀態、訂單的下單時間小於當前月份等等。
  • 角色:數據資源的主體,還能夠是用戶、部門、組織等等。

這三者配置以後獲得的一個結果就是某一類角色的某一個功能模塊對哪一個數據資源的數據規則是什麼樣的。好比有一條銷售總監的數據規則,配置銷售總監在訂單模塊裏面訂單這個實體的訂單類型是銷售訂單的全部數據,這就是針對銷售總監在訂單模塊的數據規則。可能最終數據庫存儲獲得的數據相似這樣:

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這個的結構體,但界面如何展示還有待思考。好比思路以下:

3、總結

以上只是一個設計思路,理論上來講是能夠實現的,若有不足,歡迎斧正,謝謝。若是思路沒有問題,後續博主會抽時間將這種設計的實現過程展示出來供你們參考,歡迎關注。其中的難點有兩個:

一、逐級反射EF的導航屬性,以及這個過程如何展示。是經過特性標記,仍是開發人員配置;

二、動態Expression在構造Lambda的時候和配置數據的兼容性問題,好比數據類型的兼容性有點難控制。

本文原創出處:http://www.cnblogs.com/landeanfen/

歡迎各位轉載,可是未經做者本人贊成,轉載文章以後必須在文章頁面明顯位置給出做者和原文鏈接,不然保留追究法律責任的權利

相關文章
相關標籤/搜索