通过datacontext迭代 - 最有效的方法?

时间:2014-03-05 12:53:54

标签: c# performance linq entity-framework

我已经开始在visual studio 2012中使用性能向导,因为有一个缓慢的方法,主要用于从datacontext获取所有用户。我解决了最初的问题,但我现在很好奇我是否可以加快速度。

目前我这样做:

public void GetUsers(UserManagerDashboard UserManagerDashboard)
{
    try
    {
        using (GenesisOnlineEnties = new GenesisOnlineEntities())
        {
            var q = from u in GenesisOnlineEnties.vw_UserManager_Users
                    select u;

            foreach (var user in q)
            {
                User u = new User();
                u.UserID = user.UserId;
                u.ApplicationID = user.ApplicationId;
                u.UserName = user.UserName;
                u.Salutation = user.Salutation;
                u.EmailAddress = user.Email;
                u.Password = user.Password;
                u.FirstName = user.FirstName;
                u.Surname = user.LastName;
                u.CompanyID = user.CompanyId;
                u.CompanyName = user.CompanyName;
                u.GroupID = user.GroupId;
                u.GroupName = user.GroupName;
                u.IsActive = user.IsActive;
                u.RowType = user.UserType;
                u.MaximumConcurrentUsers = user.MaxConcurrentUsers;
                u.ModuleID = user.ModuleId;
                u.ModuleName = user.ModuleName;

                UserManagerDashboard.GridUsers.users.Add(u);
            }
        }
    }
    catch (Exception ex)
    {
    }
}

Performance report from VS2012

这是一种非常直接的方法。使用实体框架连接到数据库,从视图“vw_usermanager_users”获取所有用户,并填充属于集合的对象。

我正在将int?int转换为int并且我将属性更改为?int,因此不需要强制转换。我知道这需要更长的时间,因为我正在循环记录。但是有可能加快这个查询的速度吗?

2 个答案:

答案 0 :(得分:3)

好的,首先,你的vw_UserManager_Users对象是什么样的?如果您引用的任何属性是导航属性: -

public partial class UserManager_User
{
  public string GroupName { get { return this.Group.Name; } }

  // See how the getter traverses across the "Group" relationship
  // to get the name?
}

那么你可能会先面对this issue进行操作 - 基本上你会查询你的数据库一次用于用户列表,然后一次(或更多)为每个用户加载关系。有些人在遇到问题时会想“我知道,我会使用O / RM”。现在他们有N + 1个问题。

您最好使用查询投影: -

var q = from u in GenesisOnlineEnties.vw_UserManager_Users
        select new User()
        {
          UserID = u.UserId,
          ApplicationId = u.ApplicationID,
          GroupName = u.Group.Name, // Does the join on the database instead
          ...
        };

这样,数据已经处于正确的状态,您只需要通过网络发送实际需要的列。

如果您想获得幻想,可以使用AutoMapper为您进行查询投影;节省一些冗长 - 特别是如果你在多个地方进行投影: -

var q = GenesisOnlineEnties.vw_UserManager_Users.Project().To<User>();

接下来,您使用的是什么网格?你可以使用数据绑定(或简单地替换Grid的集合)而不是用查询结果逐个填充吗?: -

UserManagerDashboard.GridUsers.users = q.ToList();

或: -

UserManagerDashboard.GridUsers.DataSource = q.ToList();

或者可能: -

UserManagerDashboard.GridUsers = new MyGrid(q.ToList());

您现在将用户添加到网格的方式就像将沙子从一个桶一次移动到另一个桶中一样。如果您正在制作桌面应用程序,那就更糟了,因为在网格中添加项目可能会触发重绘UI(即一次一粒,将桶中的每一粒都描述为每个人之后的好友)。无论哪种方式,你都在做不必要的工作,看看你的网格给你什么方法来避免这种情况。

表中有多少用户?如果数字非常大,那么您需要分页结果。确保分页发生在数据库上,而不是在你获得所有数据之后 - 否则它会破坏目的: -

q = q.Skip(index).Take(pageSize);

但请记住,有些网格会与IQueryable进行交互以进行开箱即用的分页,在这种情况下,您只需将q直接传递给网格。

这些是显而易见的。如果这不能解决您的问题,请发布更多代码,我会深入了解。

答案 1 :(得分:1)

是的,关闭更改跟踪:

var q = from u in GenesisOnlineEnties.vw_UserManager_Users.AsNoTracking()
   select u;

除非您使用实体上的所有属性,否则您也可以只选择所需的列。

var q = from u in GenesisOnlineEnties.vw_UserManager_Users.AsNoTracking()
   select new User
   {
       UserId = u.UserId,
       ...
   }