Как было выяснено, кеширование в ASP.NET MVC осуществляется с помощью атрибута OutputCache. Однако, в реальном приложении редко нужно кешировать всю страницу, чаще страница состоит из множества динамических элементов, часть из которых кешировать не нужно. Какие существуют техники и методы для решения этой задачи?
Donut caching и расширенные технологии кеширования в ASP.NET MVC
Новые ответы
Вариант 2: Частичный рендеринг и кеширование
Как известно, в ASP.NET MVC предусмотрена возможность частичного рендеринга страницы при помощи хелпера Html.RenderAction
. Этот метод встраивает в страницу результат работы определенного action-метода. Сама концепция RenderAction, пришедшая из Ruby, является порочной (не зря из Ruby ее давно выкинули), потому что перемещает логику формирования результата из контроллера в вид. Однако, есть академически чистый аналог RenderAction — это SubController из MvcContrib.
Используя RenderAction
или SubController
в связке со стандартным механизмом кеширования (атрибут OutputCache
). Можно организовать кеширование части страницы. Для этого нужно:
- выделить динамическую часть в отдельный action,
- пометить этот action атрибутом
OutputCache
- выводить результат на странице с помощью RenderAction или SubController.
К сожалению, такое решение не будет работать, потому что ASP.NET закеширует всю страницу целиком, а не ее часть. Это известная проблема, но в ASP.NET MVC 1.0 она не решена. Есть два способа борьбы с этим багом:
- Использовать решение, описанное в статье Donut Hole Caching in ASP.NET MVC. Недостатки — работает только для
WebFormsViewEngine
и требует неочевидных изменений в видах. - Отказаться от стандартного атрибута
OutputCache
и использовать свой аналогичный атрибут. Например такой, как описан в статье Partial Output Caching in ASP.NET MVC)
Вариант 1: Использовать Output Cache Substitution Feature
Атрибут OutputCache
— это надстройка над механизмом кеширования старого доброго ASP.NET (во всяком случае для MVC версии 1.0). Поэтому все расширенные техники кеширования базируются также на старых asp.net`овских фишках.
Так, частичное кеширование работает за счет механизма Output Cache Substitution Feature (Подробнее можно прочитать в классическом опусе Tip/Trick: Implement "Donut Caching" with the ASP.NET 2.0 Output Cache Substitution Feature).
Были созданы MVC-обертки над механизмом Output Cache Substitution (Еще более классический опус: Donut Caching in ASP.NET MVC). Сейчас (версия MVC 1.0) эти обертки можно найти в ASP.NET MVC Futures. Расширения и хелперы позволяют писать конструкции вида:
<!-- Это кешируется (атрибут OutputCache применен к конроллеру) -->
<%= DateTime.Now %>
<!-- Это не кешируется -->
<%= Html.Substitute(c => DateTime.Now.ToString()) %>
Однако, такой подход имеет серьезный недостаток. Делегат, который возвращает динамические данные должен работать без контекста. То есть он не может обращаться к ViewData
или TempData
. В принципе это логично, потому что когда страница достается из кеша, никаких данных, передаваемых контроллером, она уже не имеет. Отсюда следует, что Html.Substitute()
подходит только для вывода даты генерации страницы, но никак не для частичного кеширования. Есть способ подружить Html.Substitute() с TempData, но на бОльшее расчитывать не приходится.