Использование HttpContext.Current.Application для хранения простых данных

Я хочу сохранить небольшой список простого объекта (содержащего три строки) в моем приложении ASP.NET MVC. Список загружается из базы данных и редко обновляется путем редактирования некоторых значений в админке сайта.

Я подумываю использовать HttpContext.Current.Application для его хранения. Таким образом я могу загрузить его в Global.asax:

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);

    HttpContext.Current.Application["myObject"] = loadDataFromSql(); // returns my object
}

А затем можно легко ссылаться на него с любых контроллеров или представлений по мере необходимости. Затем в случае, если область администрирования вызывает действие контроллера updateMyObject, я могу просто обновить БД, загрузить ее снова и заменить HttpContext.Current.Application["myObject"].

Есть ли в этом минусы? Кажется, что это будет хорошо работать для того, чего я пытаюсь достичь, однако знает ли кто-нибудь лучший способ сделать это, если предположить, что у метода, который я изложил, есть какой-то серьезный недостаток?


person macca1    schedule 21.06.2010    source источник
comment
Что происходит при обновлении списка? Собираетесь ли вы каждый раз перезагружать приложение?   -  person Alastair Pitts    schedule 21.06.2010


Ответы (5)


На самом деле вы делаете кеширование, и это здорово, поскольку вы сокращаете количество обращений к внешнему хранилищу (базе данных или файлу, что угодно). Конечно, компромисс - это использование памяти. Теперь почти любая современная веб-платформа, включая ASP.NET, включает в себя какой-то механизм кэширования. Либо вы его используете, либо используете какую-то глобальную переменную.

Хранение данных во встроенном объекте Cache ASP.NET имеет ряд существенных преимуществ, поскольку этот механизм фактически проверяет использование памяти и удаляет кэшированные данные в соответствии с некоторыми правилами.

Однако, если данные, которые вы хотите кэшировать, интенсивно используются в приложении и их размер не слишком велик (скажем, менее 1 МБ), вы можете сохранить их как глобальную переменную.

В ASP.NET глобальные переменные достигаются либо с помощью объекта Application, как вы описали в своем вопросе, либо путем написания общедоступных статических свойств / полей во внутреннем / общедоступном классе.

Вот мое решение статических свойств. Обратите внимание, что я использую объект блокировки, чтобы защитить внутренние данные от повреждения. Это выглядит так:

public class WhateverClass
{
  private static object theLocker = new object();
  private static YourDataType theData;
  public static YourDataType TheData
  {
    get
    {
      lock (theLocker)
      {
        return theData;
      }
    }
    set
    {
      lock (theLocker)
      {
        theData = value;
      }
    }
  }
}

Использование очень простое:

Первый раз в Application_Start:

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);

    WhateverClass.TheData = loadDataFromSql();
}

В любом контроллере:

var myData = WhateverClass.TheData;

Этот подход лучше, потому что у вас есть безопасность типов, поскольку это общедоступное статическое свойство может быть явно объявлено с точным типом. Кроме того, этот тип хранилища более тестируемый, поскольку он не зависит от веб-контекста.

HTH!

person Ron Klein    schedule 21.06.2010
comment
Спасибо, это то, что я искал. - person macca1; 22.06.2010
comment
На самом деле спасибо !, это помогло мне. Но в моем случае (ASP.NET API) мне нужно хранить большие данные в памяти (с этого момента 1 ГБ (и подсчет), что составляет более 10 000 записей - для хранения в списке / словаре. Следует ли мне придерживаться вашего подхода - person Hung Doan; 06.08.2013
comment
@hungdoan, я бы использовал такое решение, как MemCache - внешний сервис кеширования. Я не думаю, что хранение более 1 ГБ для кеширования в приложении ASP.NET - хорошая идея. - person Ron Klein; 07.08.2013
comment
@RonKlein: Моя проблема: я должен работать с ними (данными) напрямую. Мне нужно читать все данные для каждого запроса (в основном для поиска / фильтрации). Поэтому я не могу использовать MemCached, что делает получение данных очень трудоемким. - person Hung Doan; 07.08.2013
comment
@hungdoan, это новое ограничение, которое вы добавляете ... И, возможно, здесь, в SOF, задают совершенно другой вопрос. Однако рассматривали ли вы БД в оперативной памяти, такую ​​как Redis? или конкретная конфигурация MySQL для хранения таблиц в памяти? таким образом у вас все еще могут быть свои данные, и вы по-прежнему можете вызывать к ним запросы. Как видите, существует довольно много решений без данных, хранящихся в приложении ASP.NET. HTH !! - person Ron Klein; 07.08.2013
comment
Нужна ли блокировка чтения? Почему бы не разрешить одновременное чтение нескольким потокам? Или эта блокировка также предназначена для того, чтобы ни один поток не читал, пока данные обновляются с помощью операции 'set'? - person Moe Howard; 15.05.2015
comment
@MoeHoward, блокировка чтения обеспечивает целостность данных (то есть данные не повреждены / неполны), как вы уже предложили в последнем случае вашего комментария. - person Ron Klein; 15.05.2015
comment
Очень просто и сэкономило мне много времени. Спасибо! Я подумал, что это может быть немного рискованно, если в приложении их много. Или это статическое свойство имеет элемент списка, который продолжает расти. Были ли у кого-нибудь проблемы в течение длительного времени? Я планировал использовать это в состоянии сеанса одного внешнего API, срок действия которого истекает каждые 25 минут. - person Spencer; 20.06.2016

HttpContext.Current.Application - это, по сути, пережиток, необходимый для обратной совместимости с классическим ASP. По сути, это статическая хеш-таблица с классической семантикой блокировки ASP (Application.Lock / Application.UnLock).

В качестве слабо типизированной Hashtable вам нужно будет преобразовывать объекты, которые вы извлекаете:

MyObject myObject = (MyObject) HttpContext.Current.Application["myObject"];

В приложении ASP.NET, которое не является переходом от классического ASP, я бы предпочел использовать другие стандартные компоненты .NET, например:

  • Статическое поле, использующее семантику блокировки .NET, если вам нужна блокировка (например, ключевое слово блокировки C # или экземпляр ReaderWriterLockSlim, в зависимости от ваших требований):

    статический MyObject myObject = LoadFromSql ();

  • Кэш ASP.NET, который имеет богатую функциональность для управления истечением срока действия, зависимостями и т. Д.

person Joe    schedule 21.06.2010

Да, использование HttpContext.Current.Application подойдет для того, что вы делаете. Нет проблем.

HttpContext.Current.Application - это просто ссылка на статический глобальный объект HttpApplicationState в .NET для вашего веб-приложения, у которого должен быть один глобальный экземпляр на каждое веб-приложение. Сохраняя данные там, вы обеспечиваете быстрый, потокобезопасный доступ к вашим глобальным переменным. Обязательно заблокируйте их при обновлении значений, как в этом примере:

System.Web.HttpContext.Current.Application.Lock();
System.Web.HttpContext.Current.Application["WebApplicationPath"] = MyWebApplicationPath;
System.Web.HttpContext.Current.Application.UnLock();

Как уже упоминали другие, вы также можете создать серию статических классов в своей App_Code или другой папке и хранить там глобальные статические значения, а также ваши HttpContext.Current.Application значения, где их можно безопасно проверять на значения или обновлять из базы данных, или обновлять и проверяют друг друга, работая в тандеме. Обычно я создаю статический глобальный класс для помощи в управлении и извлечении хранимых мной переменных приложения. Таким образом, у вас есть как словарь состояний класса HttpApplicationState, так и статические объекты веб-приложения, работающие вместе, чтобы совместно использовать и поддерживать глобальные значения. (Имейте в виду, что каждый статический класс назначается для каждого рабочего процесса, и по умолчанию на многих веб-серверах / веб-приложениях IIS может быть до 10 WP в среднем. Поэтому сведите данные в статических типах к минимуму.)

Имейте в виду, что некоторые упомянутые фермы серверов не разделяют состояние приложения. Есть много способов справиться с этим. Я не поклонник кеша из-за того, что он может истечь, выйти из строя, устареть или быть поврежден. Более простое решение - просто использовать базу данных и строки запроса URL для связи между серверами и поддержания состояния. Удачи!

person Stokely    schedule 01.03.2017
comment
Есть ли ограничение на максимальный размер данных, которые вы вводите в запись для System.Web.HttpContext.Current.Application. Например, запись размером 50 МБ? - person Heinrich; 23.09.2017

Если вы развертываете на одном веб-сервере, подход будет работать. Рассмотрим для этого объект Cache, поскольку он предоставляет больше возможностей для истечения срока действия, если вам нужна такая функциональность. (См. Сравнение, пусть и пожилого, здесь.)

Если вы когда-нибудь собираетесь развертывать на ферме веб-серверов или аналогичной, вам следует использовать memcached или другое удобное кэширование веб-фермы. механизм. Как объекты Application, так и Cache обычно существуют только в контексте одного сервера; если ваш пользователь может использовать несколько веб-серверов во время своего сеанса (и кеш должен быть идентичным), вам понадобится общий кеш, который можно увидеть с каждого из потенциальных веб-серверов.

Независимо от того, какой путь вы выберете, вам нужно будет аннулировать / перезагружать кеш всякий раз, когда изменяются базовые данные, что является настраиваемым кодом, который зависит от приложения.

Этот подход работает хорошо и может значительно ускорить процесс, но это немного больше работы, чем вы можете представить на первый взгляд ...

person Tahbaza    schedule 21.06.2010
comment
Спасибо за содержательный ответ. Я выполняю развертывание только на одном сервере, но это интересно отметить для фермы веб-серверов. - person macca1; 22.06.2010

Application_Start действительно запускается только при повторном использовании пула приложений, сбросе IIS или перезагрузке. Если вы обновляете эти значения нечасто, почему бы не сохранить их в своем web.config и получить к ним доступ таким образом?

При этом я не думаю, что в вашем подходе есть что-то неправильное. Хотя чаще я видел людей, использующих файлы конфигурации для редко изменяемых значений.

person brendan    schedule 21.06.2010