如何更有效地填充用户详细信息集合

问题描述 投票:0回答:1

我有一个

Module
班、一个
User
、一个
UserModule
和一个
UserModuleLevel
班。

_module_objects
是一个静态的
ObservableCollection
模块,在程序启动时创建,大约有10个。例如用户管理、客户服务等

User
,您可能会猜到是用户详细信息: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

c# wpf
1个回答
2
投票

为了提高性能,您可以做一些事情:

  • 更改数据访问代码以在 SQL 中执行 JOIN,以将数据作为单个结果集获取。

    SQL 在返回关系数据结果集方面往往比 C# 在事后将数据粘合在一起方面要快一些。这是因为它经过优化就是为了做到这一点,你应该利用这一点

  • 您可能应该考虑对结果进行分页 - 任何说他们一次需要所有 120,000 个结果的用户都应该用一条大鳟鱼来打头。对结果进行分页将限制您需要在应用程序中执行的处理量

执行上述操作可能非常艰巨,因为您需要修改应用程序以包含分页 - 通常第 3 方控件(例如网格等)内置了一些分页机制,并且现在大多数 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);
}

请求包含页码和页面大小(来自我的网络应用程序),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,并产生以下针对数据库的查询(在本示例中,我选择了客户列表中的第 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

这实际上取决于您已经获得了多少应用程序 - 如果您太深入,那么为 ORM 重构可能是一场噩梦,但对于其他项目来说值得考虑

© www.soinside.com 2019 - 2024. All rights reserved.