Кэширование на основе пользовательских элементов управления

171

В статье «Основы кэширования в ASP.NET» было показано, как кэшировать веб-страницу, добавляя директиву OutputCache в страницу .aspx. При таком типе кэширования, которое называется кэшированием вывода, в кэше сохраняется сгенерированная HTML-версия страницы, которую ASP.NET может автоматически повторно использовать во время последующих запросов, не выполняя ни одной строки кода страницы.

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

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

Чтобы проверить эту функциональную возможность, добавьте следующую строку в .ascx-часть пользовательского элемента управления, такого как TimeDisplay:

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="TimeDisplay.ascx.cs" 
    Inherits="TimeDisplay" %>
<%@ OutputCache Duration="10" VaryByParam="none" %>

<asp:LinkButton ID="lnkTime" runat="server" OnClick="lnkTime_Click" />

Теперь страница, которая отображает время, не будет изменяться в течение 10 секунд. Обновление страницы не оказывает никакого влияния. Параметр VaryByParam означает то же самое, что и в веб-страницах - он позволяет генерировать и кэшировать новый HTML-вывод при изменении параметров в части строки запроса URL-адреса.

Другой способ включения кэширования - добавление к объявлению класса пользовательского элемента управления атрибута PartialCaching:

[PartialCaching(10)]
public partial class TimeDisplay : System.Web.UI.UserControl
{
	// ...
}

С кэшированием фрагментов связан один недостаток. При кэшировании пользовательского элемента управления он по существу превращается в блок статической HTML-разметки. В результате объект пользовательского элемента управления окажется недоступным для кода веб-страницы. Вместо этого ASP.NET создает экземпляр одного из двух более общих типов объектов, в зависимости от того, как был создан пользовательский элемент управления.

Если пользовательский элемент управления был создан декларативно (с помощью пользовательского дескриптора в веб-странице), добавляется объект StaticPartialCachingControl. Если пользовательский элемент управления был создан программно (с помощью метода LoadControl()), добавляется объект PartialCachingControl. ASP.NET помещает объект в логическую позицию, которую пользовательский элемент управления должен был бы занимать в иерархии элементов управления страницы, если он не был кэширован.

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

Свойство VaryByControl

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

Свойство VaryByControl решает эту проблему. Оно принимает строку с разделенными точками с запятой именами элементов управления, которые применяются для изменения кэшированного содержимого, подобно тому, как свойство VaryByParameter изменяет кэшированное содержимое для значений строки запроса.

Например, рассмотрим следующий пользовательский элемент управления VaryingDate:

<%@ Control Language="C#" AutoEventWireup="true" 
    CodeFile="VaryingDate.ascx.cs" Inherits="VaryingDate" %>
<%@ OutputCache Duration="30" %>

<asp:DropDownList ID="lstMode" runat="server" Width="187px">
    <asp:ListItem>Large</asp:ListItem>
    <asp:ListItem>Small</asp:ListItem>
    <asp:ListItem>Medium</asp:ListItem>
</asp:DropDownList> <br />
<asp:Button ID="Button1" Text="Задать высоту текста" runat="server" />
<br />
<br />

Генерируемое содержимое:<br />
<asp:Label ID="TimeMsg" runat="server" />

После щелчка на кнопке она отображает текущую дату изменяя размер шрифта:

protected void Page_Load(object sender, EventArgs e)
{
        switch (lstMode.SelectedIndex)
        {
            case 0:
                TimeMsg.Font.Size = FontUnit.Large;
                break;
            case 1:
                TimeMsg.Font.Size = FontUnit.Small;
                break;
            case 2:
                TimeMsg.Font.Size = FontUnit.Medium;
                break;
        }
        TimeMsg.Text = DateTime.Now.ToString("F");
}

Хранения одной кэшированной копии этой страницы недостаточно, поскольку формат отображения изменяется в зависимости от выбора в элементе управления lstMode:

Содержимое, которое изменяется в зависимости от выбора в элементе управления

Эту проблему можно решить с помощью атрибута VaryByControl в файле .ascx пользовательского элемента управления, который ссылается на изменяющийся элемент управления:

<%@ OutputCache Duration="30" VaryByControl="lstMode" %>

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

Совместное использование кэшированных элементов управления

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

ASP.NET позволяет реализовать такой сценарий с помощью свойства Shared директивы OutputCache. Свойство Shared работает только тогда, когда директива применяется к пользовательскому элементу управления, а не к веб-форме. Например:

<%@ OutputCache Duration="30" VaryByControl="lstMode" Shared="true" %>

Этот же запрос можно выполнить, добавляя атрибут PartialCaching к объявлению класса пользовательского элемента управления:

[PartialCaching(30, null, null, null, true)]
public partial class VaryingDate : System.Web.UI.UserControl
{
	// ...
}

В этом примере параметры null представляют аргументы VaryByParam, VaryByControl и VaryByCustom.

Пройди тесты
Лучший чат для C# программистов