2015-04-08 84 views
0

我有一個Module類,一個User,一個UserModule和一個UserModuleLevel類。如何更有效地填充用戶細節的集合

_module_objects是模塊的靜態ObservableCollection,在程序啓動時創建,其中大約有10個。例如用戶管理,客戶服務等。

用戶,你可能會猜到的是用戶的詳細信息:ID,名稱等填入數據庫查詢。

使用UserModules,我不保留模塊信息在數據庫中,只是模塊級別,這只是模塊的安全級別。這保存在數據庫中:User_ID,Module_ID,ModuleLevel,ModuleLevelAccess。

我想要做的是以最快的方式填充用戶的ObservableCollection。我有大約120,000個用戶,通常這些用戶只能訪問10個模塊中的2個或3個。

下面是我到目前爲止所嘗試的,但是周圍有星號的部分是瓶頸,因爲它正在經歷每個用戶的每個模塊。

希望得到一些建議,以加快速度。

public class UserRepository 
{ 
    ObservableCollection<User> m_users = new ObservableCollection<User>(); 

    public UserRepository(){} 

    public void LoadUsers() 
    { 
     var users = SelectUsers(); 
     foreach (var u in users) 
     { 
      m_users.Add(u); 
     } 
    } 

    public IEnumerable<User> SelectUsers() 
    { 
     var userModulesLookup = GetUserModules(); 
     var userModuleLevelsLookup = GetUserModuleLevels().ToLookup(x => Tuple.Create(x.User_ID, x.Module_ID)); 

     clsDAL.SQLDBAccess db = new clsDAL.SQLDBAccess("DB_USERS"); 
     db.setCommandText("SELECT * FROM USERS"); 
     using (var reader = db.ExecuteReader()) 
     { 
      while (reader.Read()) 
      { 
       var user = new User(); 
       var userId = NullSafeGetter.GetValueOrDefault<int>(reader, "USER_ID"); 
       user.User_ID = userId; 
       user.Username = NullSafeGetter.GetValueOrDefault<string>(reader, "USERNAME"); 
       user.Name = NullSafeGetter.GetValueOrDefault<string>(reader, "NAME"); 
       user.Job_Title = NullSafeGetter.GetValueOrDefault<string>(reader, "JOB_TITLE"); 
       user.Department = NullSafeGetter.GetValueOrDefault<string>(reader, "DEPARTMENT"); 
       user.Company = NullSafeGetter.GetValueOrDefault<string>(reader, "COMPANY"); 
       user.Phone_Office = NullSafeGetter.GetValueOrDefault<string>(reader, "PHONE_OFFICE"); 
       user.Phone_Mobile = NullSafeGetter.GetValueOrDefault<string>(reader, "PHONE_MOBILE"); 
       user.Email = NullSafeGetter.GetValueOrDefault<string>(reader, "EMAIL"); 

       user.UserModules = new ObservableCollection<UserModule>(userModulesLookup); 

       //**************** BOTTLENECK ********************************** 
       foreach (var mod in user.UserModules) 
       { 
        mod.UserModuleLevels = new ObservableCollection<UserModuleLevel>(userModuleLevelsLookup[Tuple.Create(userId, mod.Module.Module_ID)]); 
       } 
       //************************************************************** 

       yield return user; 
      } 
     } 
    } 

    private static IEnumerable<Users.UserModule> GetUserModules() 
    { 
     foreach (Module m in ModuleKey._module_objects) 
     { 
      //Set a reference in the UserModule to the original static module. 
      var user_module = new Users.UserModule(m); 
      yield return user_module; 
     } 
    } 

    private static IEnumerable<Users.UserModuleLevel> GetUserModuleLevels() 
    { 
     clsDAL.SQLDBAccess db_user_module_levels = new clsDAL.SQLDBAccess("DB_USERS"); 
     db_user_module_levels.setCommandText(@"SELECT * FROM USER_MODULE_SECURITY"); 
     using (var reader = db_user_module_levels.ExecuteReader()) 
     { 
      while (reader.Read()) 
      { 
       int u_id = NullSafeGetter.GetValueOrDefault<int>(reader, "USER_ID"); 
       int m_id = NullSafeGetter.GetValueOrDefault<int>(reader, "MODULE_ID"); 
       int ml_id = NullSafeGetter.GetValueOrDefault<int>(reader, "MODULE_LEVEL_ID"); 
       int mla = NullSafeGetter.GetValueOrDefault<int>(reader, "MODULE_LEVEL_ACCESS"); 

       yield return new Users.UserModuleLevel(u_id, m_id, ml_id, mla); 
      } 
     } 
    } 
} 

最後,我會把用戶與模塊安全DataGrid中顯示,綠色顯示的按鈕有某種類型的上網本模塊的,點擊它會彈出實際的安全設置。 enter image description here

+0

你是怎麼定義你的USER_MODULE_SECURITY的?如果您只爲每個用戶存儲授予的模塊安全級別,那麼當您使用'USER_MODULE_SECURITY'加入'USERS'時,您可以只使用一個表? – Bolu

+0

[this](http://www.codeproject.com/Articles/34405/WPF-Data-Virtualization)可能會有所幫助,但需要您的努力,因爲這是一篇長篇文章。 – kennyzx

+1

聽起來像你可以很容易地在單個查詢中獲取這些信息,而不是查詢這兩個表,並在應用程序代碼中使用剪刀/粘合方法(在將事物粘合在一起時SQL可以快幾個數量級)。這需要多長時間才能完成 - 您爲什麼需要同時處理所有120,000個用戶?怎麼樣尋呼或部分負載策略? – Charleh

回答

2

提高運動效果,你可以做幾件事情:

  • 更改數據訪問代碼執行連接在SQL得到你的數據作爲單個結果集。

    SQL在返回關係數據的結果集方面比C#在將數據綁定在一起之後要快一點點。這是因爲它的優化是爲了做到這一點,你應該利用這一點,你應該利用該優勢

  • 你應該考慮分頁的結果 - 任何用戶說,他們一次需要所有120,000結果應該被打了一個大鱒魚頭。分頁結果將限制處理的,你需要在應用程序

上面做做可以說是相當艱鉅的量,你會需要修改你的應用程序,包括分頁 - 常第三方控件如網格etc有一些內置的分頁機制,而現在大多數ORM軟件都有某種尋呼支持,它可以將你的C#代碼翻譯成你選擇的RDBMS的正確方言

一個很好的例子(我最近一直在努力)是ServiceStack OrmLite。

我相信只要您使用舊版V3版本(這相當不錯)..https://github.com/ServiceStackV3/ServiceStackV3),我已經看到了在GitHub上的它的一些叉這是目前維持(http://www.nservicekit.com/

有一個小的學習曲線,但沒有任何例子/文檔不能告訴你

這裏是一個擴展方法我使用我的頁面中查詢我的服務層:

public static SqlExpressionVisitor<T> PageByRequest<T>(this SqlExpressionVisitor<T> expr, PagedRequest request) 
{ 
    return expr.Limit((request.PageNumber - 1) * request.PageSize, request.PageSize); 
} 

請求包含的頁面數量和頁面大小(從我的web應用程序),並在OrmLite的Limit擴展方法沒有休息。我應該補充說明<T>泛型參數是OrmLite在查詢後將映射到的對象類型。

這有一個(它只是一個POCO的一些註釋)爲例

[Alias("Customers")] 
public class Customer : IHasId<string> 
{ 
    [Alias("AccountCode")] 
    public string Id { get; set; } 

    public string CustomerName { get; set; } 
    // ... a load of other fields 
} 

的方法被轉換成T-SQL,並導致對DB以下查詢(在這個例子中,我選擇4頁我的客戶名單爲10的頁面大小)上:

SELECT <A big list of Fields> FROM 
(SELECT ROW_NUMBER() OVER (ORDER BY AccountCode) As RowNum, * FROM "Customers") 
AS RowConstrainedResult 
WHERE RowNum > 40 AND RowNum <= 50 

這使查詢時間降低到路不到一秒鐘,並確保我不需要寫供應商特定的SQL的shedload

這真的取決於你已經得到了多少應用程序 - 如果你太過分了,重構ORM可能是一場噩夢,但是值得考慮其他項目

+0

好吧,這可能會有點太遙遠,但是,我打算實施一個多重類型的工具來選擇一系列用戶並設置用戶模塊的安全性。如果我有內存中的用戶,這將會非常簡單,如果我正在加載和卸載用戶,這仍然是可能的嗎?我還沒有讀過很多,但這是首先想到的。 – Hank

+0

您仍然可以加載所有用戶 - 假設您只返回少量字段(例如,用戶名和許多行權限集),則120,000條記錄實際上不是那麼多。當你說你的代碼很慢時,需要多長時間才能完成用戶/權限列表的構建?這可能是因爲在.NET中手動處理,你會看到一個巨大的效率損失 – Charleh

+0

我明天會與一些數字聯繫。現在在澳大利亞的午夜 – Hank