ASP .NET获取数据太慢

时间:2016-11-04 14:23:34

标签: c# asp.net angularjs

我目前正在使用ASP.NET MVC和Angular开发Web应用程序。我希望能够做的是根据每个用户的角色获取每个用户的导航项列表,将它们全部存储在对象列表中并将其序列化为JSON以返回角度。

我的方法目前工作得很好,但是当我的页面加载时间不到一秒时,数据到达页面至少需要5秒钟。所以我的用户只是坐在那里等待导航弹弹出来并不是很好。

以下是我使用的两种方法:

public NavDirectoryViewModel GetAllUserNavItems(string UserId)
{
    NavDirectoryViewModel model = new NavDirectoryViewModel();
    model.NavItems = new List<NavViewModel>();
    foreach (var nav in GetAllNavItems())
    {
        if (GetUserNavItem(UserId, nav) != null)
        {
            if (nav.ParentId == 0)
                model.NavItems.Add(GetUserNavItem(UserId, nav));
        }
    }
    model.NavItems = model.NavItems.OrderBy(x => x.SortOrder).ToList();
    return model;
}

public NavViewModel GetUserNavItem(string UserId, NavModel model)
{

    try
    {
        if(model.AllowedUsers.FirstOrDefault(x => x.UserId.Equals(UserId)) != null || model.AllowedRoles.FirstOrDefault(x => x.Role.Users.FirstOrDefault(y => y.UserId.Equals(UserId)) != null) != null)
        {
            return new NavViewModel { Name = model.Name, Href = model.Href, Image = model.Image, SortOrder = model.SortOrder, SubItems = GetNavItems(model.Id).Where(x => x.ParentId == model.Id).Select(x => GetUserNavItem(UserId, x)).ToList() };
        }
        else
        {
            return null;
        }
    }
    catch(Exception e1) { return null; }
}

以下是两种模式:

public class NavViewModel
{
    public string Name { get; set; }
    public string Href { get; set; }
    public string Image { get; set; }
    public int? SortOrder { get; set; }

    public List<NavViewModel> SubItems { get; set; }
}

public class NavModel
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    public string Href { get; set; }
    public string Image { get; set; }//string of server image path or fa class
    public int ParentId { get; set; }//Will be 0 if it has no parent
    public int? SortOrder { get; set; }


    public virtual List<NavModel> SubItems { get; set; }
    public virtual List<RoleNavAuthorizationModel> AllowedRoles { get; set; }
    public virtual List<UserNavAuthorizationModel> AllowedUsers { get; set; }
}

Role和UserNavAuthorizationModels仅存储允许查看导航项的NavItem Id和Role / UserId。

最初我正在对NavModel进行序列化,这需要花费10-15秒,因此我制作了一个视图模型,该模型没有存储尽可能多的数据,并且加速了一些事情。但是,当其他所有内容都已加载时,我无法让我的用户等待导航栏加载。

调试时我发现GetUserNavItem方法是需要一段时间的方法。其他一切都很快就开始了。

有没有人对如何加快速度有任何建议?

更新

以下是GetAllNavItems()的代码:

public List<NavModel> GetAllNavItems() { return db.NavItems.ToList(); }

更新

我接受了Ondrej Svejdar关于缓存的建议,现在将JSON存储在用户的会话中,这样如果页面在会话处于活动状态时重新加载,则根本不需要时间重新加载。我把它放在我的控制器中:

if (HttpContext.Cache[User.Identity.GetUserId() + "NAV"] == null)
{
    string NavJSON = JsonConvert.SerializeObject(nrepo.GetAllUserNavItems(User.Identity.GetUserId()), Formatting.Indented, new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
    HttpContext.Cache[User.Identity.GetUserId() + "NAV"] = NavJSON;
    return Json(NavJSON, JsonRequestBehavior.AllowGet);
}
else
{
    string NavJSON = HttpContext.Cache[User.Identity.GetUserId() + "NAV"].ToString();
    return Json(NavJSON, JsonRequestBehavior.AllowGet);
}

1 个答案:

答案 0 :(得分:1)

好吧,你应该重新考虑一下你的模特。您正在将安全问题与实际的[导航数据]混合在一起,这就是为什么您不得不为了对用户进行身份验证而丢弃如此多的信息。

话虽这么说,你有很多链式LINQ查询,如果你看一下,可能会导致很多SQL被执行。试试这个:

public NavDirectoryViewModel GetAllUserNavItems(string userId)
{
    NavDirectoryViewModel model = new NavDirectoryViewModel();
    model.Items = GetNavItems(0, userId).OrderBy(x => x.SortOrder);
    return model;
}

private static IEnumerable<NavViewModel> GetNavItems(int parentId, string userId)
{
    List<NavViewModel> items = new List<NavViewModel>();
    var children = GetAllNavItems(parentId);
    foreach (var child in children)
    {
        if (child.IsUserAllowed(userId))
        {
            var navItem = new NavViewModel() { Name = child.Name, Href = child.Href, Image = child.Image, SortOrder = child.SortOrder };
            navItem.SubItems = GetNavItems(child.Id, userId);
            items.Add(navItem);
        }
    }
    return items;
}

您需要修改GetAllNavItems,以便它只提取您在特定阶段所需的导航项。例如,顶级菜单(ParentId = 0)。然后,您可以使用递归方法填充所有内容。

您还需要向NavModel类添加一个方法:

public bool IsUserAllowed(string userId)
{
    return AllowedUsers.Any(x => x.UserId.Equals(userId)) || AllowedRoles.SelectMany(r => r.Role.Users).Any(y => y.UserId.Equals(userId));
}

注意在LINQ查询中使用Any vs FirstOrDefault。

这些修改应该会给你不错的速度,因为你不会提取你不需要的数据,你将避免所有嵌套查询。您还在很多地方调用ToList(),导致查询执行并将数据投影到对象中。

但是,你的模型需要重新工作恕我直言。提取太多数据的方式。

我在几分钟内把它拿出来检查错误/语义,但这应该会让你对如何提高性能有所了解。