Поделиться через


Кэширование данных при запуске приложения (C#)

Скотт Митчелл

Загрузить PDF-файл

В любом веб-приложении некоторые данные будут часто использоваться, а некоторые — редко. Мы можем повысить производительность приложения ASP.NET, заранее загрузив часто используемые данные, что называется кэшированием. В этом руководстве демонстрируется один из подходов к упреждающей загрузке, который заключается в загрузке данных в кэш при запуске приложения.

Введение

В двух предыдущих руководствах рассматривается кэширование данных в уровнях представления и кэширования. В разделе Кэширование данных с помощью ObjectDataSource мы рассмотрели использование функций кэширования ObjectDataSource для кэширования данных на уровне представления. Кэширование данных в архитектуре рассмотрело кэширование в новом отдельном слое кэширования. Оба этих руководства использовали реактивную загрузку при работе с кэшем данных. При реактивной загрузке каждый раз, когда запрашиваются данные, система сначала проверяет, находится ли они в кэше. В противном случае он захватывает данные из исходного источника, например базы данных, а затем сохраняет их в кэше. Преимуществом main для реактивной загрузки является простота реализации. Одним из ее недостатков является неравномерное выполнение запросов. Представьте себе страницу, которая использует уровень кэширования из предыдущего руководства для отображения сведений о продукте. При первом посещении этой страницы или первом посещении после вытеснения кэшированных данных из-за ограничений памяти или достижения указанного срока действия данные необходимо извлечь из базы данных. Таким образом, эти запросы пользователей будут занимать больше времени, чем запросы пользователей, которые могут обслуживаться кэшем.

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

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

Примечание

Более подробное описание различий между упреждающей и реактивной загрузкой, а также списков плюсов, недостатков и рекомендаций по реализации см. в разделе Управление содержимым кэшаруководства по архитектуре кэширования для приложений платформа .NET Framework.

Шаг 1. Определение данных для кэширования при запуске приложения

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

Хотя базы данных имеют много динамических часто изменяющихся значений, большинство из них также имеют изрядный объем статических данных. Например, практически все модели данных имеют один или несколько столбцов, содержащих определенное значение из фиксированного набора вариантов. Таблица Patients базы данных может содержать PrimaryLanguage столбец, набор значений которого может быть английский, испанский, французский, русский, японский и т. д. Часто эти типы столбцов реализуются с помощью таблиц подстановки. Вместо хранения строки на английском или французском языках в Patients таблице создается вторая таблица, которая обычно содержит два столбца — уникальный идентификатор и описание строки — с записью для каждого возможного значения. В PrimaryLanguage столбце Patients таблицы хранится соответствующий уникальный идентификатор в таблице подстановки. На рис. 1 основным языком пациента Джона Доу является английский, а у Эда Джонсона — русский.

Таблица Languages — это таблица подстановки, используемая таблицей пациентов.

Рис. 1. Таблица Languages является таблицей подстановки, используемой таблицей Patients

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

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

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

Шаг 2. Изучение различных способов кэширования данных

Сведения можно программно кэшировать в ASP.NET приложении, используя различные подходы. Мы уже видели, как использовать кэш данных в предыдущих руководствах. Кроме того, объекты можно кэшировать программным способом с помощью статических элементов или состояния приложения.

При работе с классом, как правило, сначала необходимо создать экземпляр класса, прежде чем получить доступ к его членам. Например, чтобы вызвать метод из одного из классов на уровне бизнес-логики, необходимо сначала создать экземпляр класса :

ProductsBLL productsAPI = new ProductsBLL();
productsAPI.SomeMethod();
productsAPI.SomeProperty = "Hello, World!";

Прежде чем вызывать SomeMethod или работать с SomeProperty, необходимо сначала создать экземпляр класса с помощью new ключевое слово. SomeMethod и SomeProperty связаны с определенным экземпляром. Время существования этих элементов привязано к времени существования связанного с ними объекта. Статические члены , с другой стороны, являются переменными, свойствами и методами, которые являются общими для всех экземпляров класса и, следовательно, имеют время существования, дольше, чем класс. Статические элементы обозначаются ключевое слово static.

Помимо статических элементов, данные можно кэшировать с помощью состояния приложения. Каждое приложение ASP.NET поддерживает коллекцию имен и значений, доступную всем пользователям и страницам приложения. Доступ к этой коллекции можно получить с помощью HttpContext свойства классаApplication и использовать из класса кода программной части ASP.NET страницы следующим образом:

Application["key"] = value;
object value = Application["key"];

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

Шаг 3. КэшированиеSuppliersданных таблицы

Таблицы базы данных Northwind, которые мы реализовали на сегодняшний день, не включают в себя традиционные таблицы подстановки. Четыре таблицы DataTable, реализованные во всех таблицах модели DAL, значения которых не являются статическими. Вместо того, чтобы тратить время на добавление новой таблицы DataTable в DAL, а затем нового класса и методов в BLL, в этом руководстве давайте просто сделаем вид, что Suppliers данные таблицы являются статическими. Поэтому эти данные можно кэшировать при запуске приложения.

Для начала создайте класс с именем StaticCache.cs в папке CL .

Создание класса StaticCache.cs в папке CL

Рис. 2. Создание StaticCache.cs класса в папке CL

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

[System.ComponentModel.DataObject]
public class StaticCache
{
    private static Northwind.SuppliersDataTable suppliers = null;
    public static void LoadStaticCache()
    {
        // Get suppliers - cache using a static member variable
        SuppliersBLL suppliersBLL = new SuppliersBLL();
        suppliers = suppliersBLL.GetSuppliers();
    }
    [DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
    public static Northwind.SuppliersDataTable GetSuppliers()
    {
        return suppliers;
    }
}

В приведенном выше коде используется статическая переменная-член suppliers, для хранения результатов SuppliersBLL из метода класса GetSuppliers() , который вызывается из LoadStaticCache() метода . Метод LoadStaticCache() должен вызываться во время запуска приложения. После загрузки этих данных при запуске приложения любая страница, которая должна работать с данными StaticCache поставщика, может вызвать метод класса GetSuppliers() . Таким образом, вызов базы данных для получения поставщиков происходит только один раз при запуске приложения.

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

[System.ComponentModel.DataObject]
public class StaticCache
{
    public static void LoadStaticCache()
    {
        // Get suppliers - cache using application state
        SuppliersBLL suppliersBLL = new SuppliersBLL();
        HttpContext.Current.Application["key"] = suppliersBLL.GetSuppliers();
    }
    [DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
    public static Northwind.SuppliersDataTable GetSuppliers()
    {
        return HttpContext.Current.Application["key"] as Northwind.SuppliersDataTable;
    }
}

В LoadStaticCache()сведения о поставщике хранятся в ключе переменной приложения. Он возвращается в качестве соответствующего типа (Northwind.SuppliersDataTable) из GetSuppliers(). Хотя доступ к состоянию приложения можно получить в классах кода программной части ASP.NET страниц с помощью Application["key"], в архитектуре необходимо использовать HttpContext.Current.Application["key"] для получения текущего HttpContext.

Аналогичным образом кэш данных можно использовать в качестве хранилища кэша, как показано в следующем коде:

[System.ComponentModel.DataObject]
public class StaticCache
{
    public static void LoadStaticCache()
    {
        // Get suppliers - cache using the data cache
        SuppliersBLL suppliersBLL = new SuppliersBLL();
        HttpRuntime.Cache.Insert(
          /* key */                "key", 
          /* value */              suppliers, 
          /* dependencies */       null, 
          /* absoluteExpiration */ Cache.NoAbsoluteExpiration, 
          /* slidingExpiration */  Cache.NoSlidingExpiration, 
          /* priority */           CacheItemPriority.NotRemovable, 
          /* onRemoveCallback */   null);
    }
    [DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
    public static Northwind.SuppliersDataTable GetSuppliers()
    {
        return HttpRuntime.Cache["key"] as Northwind.SuppliersDataTable;
    }
}

Чтобы добавить элемент в кэш данных без истечения срока действия на основе времени, используйте значения и в System.Web.Caching.Cache.NoAbsoluteExpirationSystem.Web.Caching.Cache.NoSlidingExpiration качестве входных параметров. Эта конкретная перегрузка метода кэша данных была выбрана Insert , чтобы можно было указать приоритет элемента кэша. Приоритет используется для определения элементов, которые следует удалять из кэша при нехватке доступной памяти. Здесь мы используем приоритет NotRemovable, который гарантирует, что этот элемент кэша не будет очищаться.

Примечание

Скачиваемый из этого руководства класс реализуется StaticCache с использованием подхода со статическими переменными-членами. Код для методов кэширования состояния приложения и данных доступен в комментариях в файле класса.

Шаг 4. Выполнение кода при запуске приложения

Чтобы выполнить код при первом запуске веб-приложения, необходимо создать специальный файл с именем Global.asax. Этот файл может содержать обработчики событий уровня приложения, сеанса и запроса. Здесь можно добавить код, который будет выполняться при каждом запуске приложения.

Добавьте файл в Global.asax корневой каталог веб-приложения, щелкнув правой кнопкой мыши имя проекта веб-сайта в Обозреватель решений Visual Studio и выбрав команду Добавить новый элемент. В диалоговом окне Добавление нового элемента выберите тип элемента Класс глобального приложения и нажмите кнопку Добавить.

Примечание

Если в проекте уже есть Global.asax файл, тип элемента Global Application Class не будет указан в диалоговом окне Добавление нового элемента.

Добавьте файл Global.asax в корневой каталог веб-приложения.

Рис. 3. Добавление файла в Global.asax корневой каталог веб-приложения (щелкните для просмотра полноразмерного изображения)

Шаблон файла по умолчанию Global.asax включает пять методов в теге на стороне <script> сервера:

  • Application_Start выполняется при первом запуске веб-приложения
  • Application_End выполняется при завершении работы приложения
  • Application_Error выполняется всякий раз, когда необработанное исключение достигает приложения.
  • Session_Start выполняется при создании нового сеанса.
  • Session_End выполняется при истечении срока действия или прерывании сеанса

Обработчик Application_Start событий вызывается только один раз в течение жизненного цикла приложения. Приложение запускается при первом запросе ресурса ASP.NET из приложения и продолжает выполняться до перезапуска приложения, что может произойти путем изменения содержимого /Bin папки, изменения Global.asax, изменения содержимого в App_Code папке или изменения Web.config файла, а также других причин. Дополнительные сведения о жизненном цикле приложения см. в ASP.NET Обзор жизненного цикла приложения.

Для работы с этими учебниками нам нужно только добавить код в Application_Start метод , поэтому вы можете удалить остальные. В Application_Startпросто вызовите StaticCache метод класса LoadStaticCache() , который будет загружать и кэшировать сведения о поставщике:

<%@ Application Language="C#" %>
<script runat="server">
    void Application_Start(object sender, EventArgs e) 
    {
        StaticCache.LoadStaticCache();
    }
</script>

Вот и все! При запуске LoadStaticCache() приложения метод заберет сведения о поставщике из BLL и сохранит их в переменной статического члена (или в любом кэше, которое вы использовали в StaticCache классе ). Чтобы проверить это поведение, установите точку останова в методе Application_Start и запустите приложение. Обратите внимание, что точка останова достигается при запуске приложения. Однако последующие запросы не приводят к выполнению Application_Start метода.

Использование точки останова для проверки выполнения обработчика событий Application_Start

Рис. 4. Использование точки останова для проверки Application_Start выполнения обработчика событий (щелкните для просмотра полноразмерного изображения)

Примечание

Если вы не достигли точки останова Application_Start при первом запуске отладки, это связано с тем, что приложение уже запущено. Принудительная перезагрузка приложения, изменив Global.asax файлы или , Web.config а затем повторите попытку. Вы можете просто добавить (или удалить) пустую строку в конце одного из этих файлов, чтобы быстро перезапустить приложение.

Шаг 5. Отображение кэшированных данных

На этом этапе StaticCache класс имеет версию данных поставщика, кэшированную при запуске приложения, доступ к которому можно получить с помощью метода GetSuppliers() . Для работы с данными из уровня представления можно использовать ObjectDataSource или программно вызвать StaticCache метод класса GetSuppliers() из класса кода программной части ASP.NET страницы. Рассмотрим использование элементов управления ObjectDataSource и GridView для отображения кэшированных сведений о поставщике.

Начните с открытия страницы AtApplicationStartup.aspx в папке Caching . Перетащите элемент GridView с панели элементов в конструктор, задав для его ID свойства значение Suppliers. Затем в смарт-теге GridView выберите создать объект ObjectDataSource с именем SuppliersCachedDataSource. Настройте ObjectDataSource для использования StaticCache метода класса GetSuppliers() .

Настройка ObjectDataSource для использования класса StaticCache

Рис. 5. Настройка ObjectDataSource для использования StaticCache класса (щелкните для просмотра полноразмерного изображения)

Получение кэшированных данных поставщика с помощью метода GetSuppliers()

Рис. 6. Получение кэшированных данных поставщика с помощью GetSuppliers() метода (щелкните для просмотра полноразмерного изображения)

После завершения работы мастера Visual Studio автоматически добавит BoundFields для каждого поля данных в SuppliersDataTable. Декларативная разметка GridView и ObjectDataSource должна выглядеть примерно так:

<asp:GridView ID="Suppliers" runat="server" AutoGenerateColumns="False" 
    DataKeyNames="SupplierID" DataSourceID="SuppliersCachedDataSource" 
    EnableViewState="False">
    <Columns>
        <asp:BoundField DataField="SupplierID" HeaderText="SupplierID" 
            InsertVisible="False" ReadOnly="True" 
            SortExpression="SupplierID" />
        <asp:BoundField DataField="CompanyName" HeaderText="CompanyName" 
            SortExpression="CompanyName" />
        <asp:BoundField DataField="Address" HeaderText="Address" 
            SortExpression="Address" />
        <asp:BoundField DataField="City" HeaderText="City" 
            SortExpression="City" />
        <asp:BoundField DataField="Country" HeaderText="Country" 
            SortExpression="Country" />
        <asp:BoundField DataField="Phone" HeaderText="Phone" 
            SortExpression="Phone" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="SuppliersCachedDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetSuppliers" TypeName="StaticCache" />

На рисунке 7 показана страница при просмотре в браузере. Выходные данные совпадают, если мы извлекли данные из класса BLL SuppliersBLL , но с помощью StaticCache класса возвращается данные поставщика в кэше при запуске приложения. Вы можете задать точки останова в методе StaticCache класса, GetSuppliers() чтобы проверить это поведение.

Кэшированные данные поставщика отображаются в GridView

Рис. 7. Кэшированные данные поставщика отображаются в GridView (щелкните, чтобы просмотреть полноразмерное изображение)

Сводка

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

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

Счастливое программирование!

Об авторе

Скотт Митчелл (Scott Mitchell), автор семи книг ASP/ASP.NET и основатель 4GuysFromRolla.com, работает с Веб-технологиями Майкрософт с 1998 года. Скотт работает независимым консультантом, тренером и писателем. Его последняя книга Sams Teach Yourself ASP.NET 2.0 в 24 часа. Его можно связать по адресу mitchell@4GuysFromRolla.com. или через его блог, который можно найти по адресу http://ScottOnWriting.NET.

Отдельная благодарность

Эта серия учебников была проверена многими полезными рецензентами. В качестве ведущих рецензентов в этом руководстве были Тереса Мерфи и Зак Джонс. Хотите ознакомиться с моими предстоящими статьями MSDN? Если да, опустите мне строку в mitchell@4GuysFromRolla.com.