2015-04-08 27 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