2017-04-20 28 views
0

我已經搜索了這個答案,但找不到最細微的建議或信息。我應該注意到這是一個運行在IIS上的ASP.Net 4.5.2 webforms應用程序。存儲/使用一個大的靜態集合

TL; DR - ASP.Net如何存儲大量靜態集合,並且是否有搜索特定項目的最佳實踐?

我有一個靜態的對象集合,只會在開發過程中進行更改。在運行期間,集合將永遠不會改變,並將向所有用戶呈現相同的對象。我計劃實施如下:

public class Widget 
{ 
    public Int32 Id { get; set; } 
    public string Title { get; set; } 
    public string Description { get; set; } 
    public string ApiUrl { get; set; } 
} 

public static class AvailableWidgets 
{ 
    public static List<Widget> LoadWidgets() 
    { 
     return new List<Widget>() 
     { 
      new Widget() { Id = 1, Title = "Foo", Description = ".", ApiUrl = "/api/Foo" }, 
      new Widget() { Id = 2, Title = "Bar", Description = "..", ApiUrl = "/api/Bar" }. 
      new Widget() { Id = 3, Title = "FooBar", Description = "...", ApiUrl = "/api/FooBar" } 
     }; 
    } 
} 

該列表可能會變得非常大(也有很多屬性,和額外的列表作爲顯示類的屬性)。我打算將這些數據轉移到SQL Server中,但這會帶來它自己的開銷以及CRUD代碼。

我有以下問題:

  1. 如何ASP.Net存儲大量靜態的集合?這些是否在內存中?如果集合的大小不利於可用內存(它是否放置在物理磁盤上)會發生什麼?

  2. 如果我選擇上述方法,如果我需要在集合中找到特定項目,例如

    Widget widget1 = (from w in AvailableWidgets.LoadWidgets() where w.Id == 2352 select w).FirstOrDefault();

    ...是不好的做法,首先檢索整個集合?有沒有辦法解決這個問題?

  3. 同樣,如果我只需要一個特定的屬性值從一個單一的項目,這是一個大型集合可怕嗎?

    string title = (from w in AvailableWidgets.LoadWidgets() where w.Id == 123 select w).FirstOrDefault().Title;

從經驗豐富的listoholic任何指導,將是非常歡迎的。

+1

從我看到你創建並每次調用LoadWidgets時返回一個新列表。這是非常低效的。第一個改進是將該列表轉換爲單例。請查看['Lazy '](https://msdn.microsoft.com/en-us/library/dd986615(v = vs.110).aspx)class以獲取更多信息。 –

+1

如果你有很多東西,我建議不要使用'List',更好的'HashSet'或'Dictionary'。此外,由於您有共享訪問權限,所以最好使用['ConcurrentDictionary'](https://msdn.microsoft.com/en-us/library/dd287191(v = vs.110).aspx) – DavidG

+0

看看這些新的類型,謝謝。我不認爲這四個人中有一個是最好的? @FedericoDipuma我應該創建集合作爲一個靜態屬性什麼的?不確定如何避免每次創建新的集合。 – EvilDr

回答

2

ASP.Net如何存儲大量靜態集合?這些是否在內存中?如果集合的大小不利於可用內存(它是否放置在物理磁盤上)會發生什麼?

靜態字段和其他類型的變量之間沒有區別,它們總是存儲在內存中。這意味着如果您的Web應用程序佔用太多內存,它將由操作系統處理(也可能是作爲虛擬內存複製到磁盤中的一部分內存)。

如果你確實需要這些數據是靜態的,那麼我將按照註釋中所述的方式使用正確的數據類型,以便您可以輕鬆訪問其成員,而不會冒着內存泄漏的風險,每次訪問時創建集合。

AvailableWidgets類可以被改變,以這樣的:

public static class AvailableWidgets 
{ 
    private static readonly Lazy<Dictionary<int, Widget>> LazyWidgets = new Lazy<Dictionary<int, Widget>>(LoadWidgets); 

    public static IEnumerable<Widget> All => LazyWidgets.Value.Values; 

    public static Widget GetById(int id) 
    { 
     Widget widget; 
     if (LazyWidgets.Value.TryGetValue(id, out widget)) 
      return widget; 
     return null; 
    } 

    private static Dictionary<int, Widget> LoadWidgets() 
    { 
     return new Dictionary<int, Widget>() 
     { 
      {1, new Widget() {Id = 1, Title = "Foo", Description = ".", ApiUrl = "/api/Foo"}}, 
      {2, new Widget() {Id = 2, Title = "Bar", Description = "..", ApiUrl = "/api/Bar"}}, 
      {3, new Widget() {Id = 3, Title = "FooBar", Description = "...", ApiUrl = "/api/FooBar"}} 
     }; 
    } 
} 

這樣做有很多好處:

  • 的小工具詞典在整個應用程序生命週期只初始化一次(當它被用於訪問第一次);
  • 通過在內部使用字典,您可以通過O(1)中的id訪問單個Widgets(您不再需要枚舉集合);
  • 如果您需要查詢所有小部件,您可以使用All屬性進行查詢,該屬性公開IEnumerable接口。

你的應用程序中,你可以使用它像這樣:

//single item 
var widget = AvailableWidgets.GetById(id); 

//single property 
var title = widget?.Title; 

//query 
var matchingWidgets = AvailableWidgets.All.Where(w => w.Title.Contains("Foo")); 

關於底層集合類型,你說:

在運行期間,收集永遠不會改變,並且將向所有用戶呈現相同的對象

然後你會發現使用ConcurrentDictionary沒有任何好處,它只會減緩其元素的訪問,因爲它的開銷。只有計劃在多個線程運行時訪問並修改(添加,刪除,更改)集合時纔有用。

+0

好的先生,這是最好的答案的最好例子!謝謝! – EvilDr