2016-11-04 42 views
2

我目前正在使用ASP.NET MVC和Angular開發一個Web應用程序。我希望能夠做的是根據每個用戶的角色獲取導航項目列表,將它們全部存儲在對象列表中,並將其序列化爲JSON以返回角度。ASP .NET獲取數據太慢

我的方法目前工作得很好,但數據至少需要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; } 
} 

中的作用和UserNavAuthorizationModels只存儲NavItem Id和角色/用戶ID是允許查看導航項目。

最初我正在序列化需要10-15秒的NavModel,所以我製作了一個視圖模型,它沒有存儲儘可能多的數據,並且稍微加快了速度。但是,我無法讓我的用戶在加載其他所有內容時等待導航欄加載。

調試時,我發現它是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); 
} 
+2

第一個問題是,你調用'GetUserNavItem'兩次,一次是在'如果(GetUserNavItem(用戶ID,NAV)!= NULL)'然後再'如果(nav.ParentId == 0)',你可以做該調用一旦將其分配給一個變量,然後使用變量值而不是再次調用該方法。 – Corporalis

+1

如果您分享更多的代碼,這將有所幫助。一般來說,最好的做法是a)從數據庫加載所有可能的導航節點並緩存它 - 在我看來,您正在逐節點加載它b)​​按用戶請求從緩存的節點列表中選擇合適的節點(在a)中檢索以構建導航樹 –

+0

其他問題是在此處調用GetUserNavItem和GetNavItems的遞歸,這將運行這些昂貴的查詢......它需要進行重大的重構。還有其他更有效的方法來存儲/構建導航樹。然而,在這種情況下緩存應該至少可以幫助你如何做到這一點。 –

回答

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查詢使用任何VS FirstOrDefault。

此修改應該給你一個體面的速度,因爲你不會拉着你不需要的數據,你會被避免所有嵌套查詢。您還在很多地方調用了ToList(),這會導致查詢執行並將數據投影到對象中。

不過,您的模型需要重新編制恕我直言。太多的數據被拉。

我在幾分鐘內掀起了這一點,所以檢查錯誤/語義,但是這應該給你的如何提高性能是一個好主意。

+0

這太棒了!它加快了大約一兩秒,好多了!感謝您的建議。 – DevShadow

+0

我很高興我能幫上忙。 :-) – JuanR