存储/使用大型静态集合

时间:2017-04-20 14:42:45

标签: c# asp.net .net webforms

我已经搜索过这个问题的答案,但是找不到一点点明确的建议或信息。我应该注意这是一个在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;

  4. 非常欢迎来自经验丰富的专业人士的任何指导。

1 个答案:

答案 0 :(得分: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"}}
        };
    }
}

这有很多好处:

  • Widgets字典仅在整个应用程序生命周期内初始化一次(第一次访问时);
  • 通过在内部使用字典,您可以在O(1)中按ID访问单个窗口小部件(您不再需要枚举该集合);
  • 如果您需要查询所有小部件,可以使用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没有任何好处,因为它的开销只会减慢对其元素的访问速度。仅当您计划在运行时通过多个线程访问然后修改(添加,删除,更改)集合时,它才有用。