Прощай, ViewState!

.NET*

Проблема


С самого начала изучения технологии ASP.NET меня неприятно удивили две вещи: наличие (точнее его огромный размер на сложных страницах) состояния представления (ViewState) в виде скрытого поля на странице и именование (ID) контейнеров серверных элементов. После PHP, где под контролем находится каждый выводимый в браузер символ, это было разочарованием.

Ну, с проблемой именования я более-менее смирился, в конце-концов стили для элементов можно применять с помощью class (тем более, что это работает быстрее, чем по id), а для использования id в клиентских скриптах можно вывести свойство ClientID серверного контрола.

ViewState же нужно было обуздать :) Ведь занимающее от 50% размера страницы состояние представления — это не очень хорошо с точки зрения клиентской оптимизации. Плюс к увеличению размера страницы, с позиции SEO тоже на таких страницах не все радужно. Могу сделать предположение, даже не будучи специалистом по поисковикам, что страница с <h1>нужный текст</h1> ранжируется лучше, чем <80кб какой-то фигни><h1>нужный текст</h1> (поправьте меня, если не прав).

Что можно сделать?


Дино Эспозито в своей книге «ASP.NET 2.0. Базовый курс» (гл. 13) предлагает два варианта решения проблемы: отключение ViewState у элементов, для которых его отсутствие не нарушит работу приложения и перенос состояния представления на сервер.

С отключением ViewState все прозрачно, а вот второй способ рассмотрю подробно.

Итак, нам предлагается создать класс, производный от Page, переопределить в нем два метода (сохранения SavePageStateToPersistenceMedium и загрузки LoadPageStateFromPersistenceMedium состояния представления) и реализовать способ сохранения/считывания ViewState вручную. Затем все свои страницы нужно наследовать от этого класса.

Скажите, кто этим занимался когда-нибудь? ;)

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

Начиная с версии 2.0 в ASP.NET появился механизм адаптеров. С помощью этого механизма можно изменять поведение элементов управления (и страниц тоже), не затрагивая их код.

Создаем адаптер для страницы:
namespace Samples.AspNet.CS {

  using System.Web.UI;

  public class MyPageAdapter : System.Web.UI.Adapters.PageAdapter {

    public override PageStatePersister GetStatePersister() {
      return new SessionPageStatePersister(Page);
    }
  }
}


* This source code was highlighted with Source Code Highlighter.

В этом адаптере переопределен метод GetStatePersister, который возвращает объект, используемый веб-страницей для настройки элементов управления и состояний представления. По умолчанию используется HiddenFieldPageStatePersister (хранение ViewState в скрытом поле в браузере), меняем его на SessionPageStatePersister (хранение ViewState в сессии на сервере).

Теперь нужно наш адаптер поключить к страницам.

В папке App_Browsers создаем файл default.browser:
<browsers>
  <browser refID="Default">
    <controlAdapters>
      <adapter controlType="System.Web.UI.Page" adapterType="Samples.AspNet.CS.MyPageAdapter" />
    </controlAdapters>
  </browser>
</browsers>


* This source code was highlighted with Source Code Highlighter.

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

Пробуем!

P.S. Желательно все же отключать ViewState в любом случае на элементах, где он не нужен.

Используемая информация:

+20
21 мая 2009, 22:31
29
alemiks 1,8

комментарии (76)

–21
vorbiz #
Можете загонять меня в минуса и дальше. Но! Б-же, как на этом можно программировать? Я смотрю на свой код руби, архитектуру рельсов и просто плачу от осознания того, насколько мне повезло в этой жизни. Простите, я должен был высказаться.
*Приходите в нашу церковь* :)
+13
RedOctober #
Как как — очень просто. Любая технология в умелых руках и трезвой голове будет делать то что вам нужно.

p.s. я Ruby не знаю, но думаю что обязательно гляну…
+10
vorbiz #
Поверьте, это стоит того.
*Брошюрочку бесплатную не желаете почитать?* :)
+14
Error_403_Forbidden #
Задолбали уже холиварить. Самые ненавистные холиварщики — пэхаписты, рубисты и юниксоиды.
Выбрал себе язык по вкусу — молодец, сиди в уголке и молчи в тряпочку
–12
ferrari #
кстати php, ruby, pithon — не сильно между собой цапаются, потому что идеологию построения проекта можно легко перенять друг у друга, в отличие от ох… нных пацанов из майкрософт, которые всю жизнь брали не качеством, а количеством и навязыванием своей единственно верной теории. Если вам нравится те рамки, в которые вас ставит эта контора — будьте здоровы и цитирую: «молодец, сиди в уголке и молчи в тряпочку», а о холиваре речь тут не шла.

ps: чтоб я туда вернулся… да нафик нужно.
+4
Error_403_Forbidden #
Чувак, ты продолжаешь холиварить. Прекрати это.
+3
jaffa #
Дело в том, что ASP.NET Web Forms создавался как аналог Win Forms. Соответственно и модель самого веб приложения была притянула «за уши», за все надо платить. Как альтернатива Web Forms есть MVC.
–3
ferrari #
Впринципе фиолетово в чем дело, есть инструмент — будьте добры модернизируйте в ногу со временем.
<никогда не оправдывайся — друзьям это не нужно, а враги всё равно не поверят.>
+8
hannimed #
Они и модернизируют. Версии C# и ASP.NET выходят чаще чем того же PHP…
0
jaffa #
Судя по вашему настрою — вы сюда повоевать пришли.
–16
vorbiz #
Да, я в курсе, спасибо. mvc от.нета ещё только пока не пробовал. Надо будет посмотреть, чем вас там кормят.
*приходите на наши собрания и станете на один шаг ближе к просветлению* :)
+4
kem #
Чаще всего так говорит тот, кто знает только одну технологию из двух. Вы на .net пробовали писать, прежде, чем сравнивать?

Возможно, после изучения, у вас сильно убавится максимализм при сравнении.
–5
vorbiz #
Я на асп.нет писал на уровне «допилить проект, чтобы всё работало». Долго приходил в себя от ужаса. А вот Вы на руби писали?
.нет очень хорош для написания десктопных приложений, но использовать его для веба — увольте.
*не могли бы Вы дать немного денег на развитие церкви?* :)
+5
kottt #
Ну конечно. Вот схватился человек за неизвестный язык. Глянул в неизвестно кем и как написанный проект, ничего не понял, книжек не открыл и теперь ругается и поносит отличную технологию.

Я вот тоже посмотрел на кусок кода php, о**ел от замечательного использования везде переменных без явного указания типов (пока разберешься — мозг сломается), да еще и господин, кхм, кодер, который писал это, использовал одни и те же переменные для разных типов данных. Совершенно не оправдано. Типа, зачем впустую новых создавать, если и старые остались. Просто восхитительно.

А технологии Майкрософт ох как совершенствуются. А если кто-то хочет писать не по последнему слову техники, так они еще и очень гибкие и легко подстраиваемые под цели разработки.
–1
vorbiz #
Вот Вы интересный человек, а за руби я схватился, изначально зная, что он крут? Точно так же, ничего не зная о нём, и так же смотрел на неизвестно кем написаные проекты, разобраться в которых почему-то не составило никакого труда, а тем более их дописать. И книжку я читал. Я правда знаю, о чём говорю.
0
mlurker #
Может, кодерам легче разбираться в кодерском коде?
+1
myiworm #
Уточните, что вас довело до ужаса в дотнете, а вернее в шарпе(если вы конечно не на бейсике проект допиливали)?
+8
XaocCPS #
вы наверное сильно удивитесь, узнав, что asp.net поддерживает и Ruby/Rails (IronRuby) и Python (IronPython) языки, на которых можно писать веб-приложения. Кстати, IronRuby на днях обновилось, сообщают что на 80% проходит тест RubySpec, думаю для версии 0.5 совсем неплохо. Допилят, будет совсем чудесно

.net-платформа тем и уникальна, что позволяет писать на чем угодно и даже вмешивать все вместе в одном проекте. Так что, лучше это вы к нам приходите.
–17
vorbiz #
.нет-то уникальна, но от этого менее уродливой она не становится. Повторюсь, для десктопа оно очень даже неплохо, но для веба стандартом де-факто является реализация mvc в ror.
+6
XaocCPS #
«уродливая» в чем, вы можете привести какие-нибудь уродства ASP.NET MVC?

это ваша личная оценка «стандарт де факто»? или есть какие-то исследования?
–2
vorbiz #
Я говорил про .net платформу и webforms. Я ничего не сказал про mvc. MVC я ещё не пробовал. Оценка стандарта де-факто моя личная. Я видел много попыток повторить то, что сделали 37signals, но пока достойного ничего не вышло.
0
XaocCPS #
хорошо, расскажите про любые «уродства», которые вы видите в .net
–3
vorbiz #
.net, применительно к вебу. уродлив. статья о чём? об уродстве дотнета, который не позволяет нормально контролировать вывод и выдаёт кучу хлама в браузер. Приходится применять разного рода хаки… нет не может удобно применяться в веб, или Вы с этим не согласны? Может согласны, но Вы пытаетесь что-то доказать из принципа? Если это так, то признайтесь и не тратьте моё время. Если это не так и Вы свято верите, что.нет и webforms можно с успехом использовать для командной разработки мало-мальски крупного проекта — то Вы сильно ошибаетесь. MVC, возможно и будет хорошей альтернативой, но только когда его окончательно доработают. А пока идеи, работающие в рельсах, такие как система миграций, rest, соглашения вместо конфигов, множество свободных плагинов, которые делают разработку эффективной и удобной.
Я много раз видел, как приходили новые люди дописывать проект на asp.net и как они кричали, что всё нужно ломать и делать заново. С рельсами такого не случается.
Ещё раз повторюсь:.нет, как платформа для десктопа очень хороша и удобна, но нечего в веб лезть с этой громадиной.
+3
XaocCPS #
я вам ничего не доказываю, пока только вы пытаетесь убедить меня web-разработчика на .net, что я что-то не то делаю.

хаков никаких не нужно, есть одна настройка EnableViewState=«false», статья про то как сохранить ViewState на сервере, который зачем-то кому-то может понадобится.

вообще, это спор глухого со слепым. вы уперлись в свою руби и больше ничего знать не хотите, я на руби не писал и оценивать его не могу. Так что, с вашего разрешения, хочу закрыть дискуссию.
–8
vorbiz #
Вот это правильно. Только я на аспе писал и дописывал проекты. И могу оценивать.
+2
sse #
>> что.нет и webforms можно с успехом использовать для командной разработки мало-мальски крупного проекта — то Вы сильно ошибаетесь

Очень громкие заявления для человека, который сам признал, что с asp.net знаком «абы как», вы не находите?
+1
Smerig #
.net — это всего лишь фреймворк. Если по вашим убеждениям, то это асп.нет уродлив, но никак не сам .net
0
se_pavel #
уродство сильное слово, но вот, например, код из чужого проекта, код явно ничего полезного не делает, но его много и его обычно больше, чем для Rails решениях, что меня смущает всегда в ASP.NET проектах

using System;
using System.Collections.Generic;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Paper.DataHelpers.NavigationConfiguration;

public partial class CMS_CMSNavigationControl: NavigationControl
{
protected NavigationConfiguration navigation = null;
protected NavigationNodeConfiguration currentSection = null;

protected NavigationNodeConfiguration currentNode = null;
protected void Page_Load(object sender, EventArgs e)
{
navigation = (NavigationConfiguration)ConfigurationManager.GetSection(«cmsNavigation»);

currentNode = navigation.FindNodeByUrl(Request.Path.Replace(Paths.BackendPath, ""));
if (currentNode != null)
{
currentSection = currentNode.Parent;
}
}
}
0
sse #
За этот код скажите отдельно спасибо тому, кто не понимает разницы между M, V и C в MVC модели. Дотнет тут не при чем.

Ну а если вам кажется, что «этот их» c# слишком синтаксически мусорный, возьмите Boo (Бу), и используйте его. Boo — почти что питон по синтаксису, и по красоте кода не сильно от руби оторвался.

words = @/ /.Split(«Some long string») # пользуюсь регэкспом прямо в языке
for word in words:
 print word
0
XaocCPS #
это плохонаписанный код, хотя бы потому, что из 12 первых строк на деле нужно оставить только пару. Кто и зачем оставил остальные — непонятно, надавайте ему по рукам.
+1
hannimed #
Вам, 1 в 1, могу сказать ваши же слова :)
*Приходите в нашу церковь* :)
+1
alexiznot #
Обожаю рельсы, пишу на них для удовольствия. Работаю прогером в Риге, на Асп.нет. Потому что за это платят намного больше. Рынок диктует-с. В нашей отсталой стране о руби даже мало кто слышал.
+6
Pavel7 #
Вы абсолютно правы. Пожалуйста, продолжайте и дальше писать на руби и не суйтесь в дотнет.
0
butaji #
Солидарен с Вами на все 100, однако подача предложения «переходить в вашу церковь» не совсем корректна.
+3
Gangsta #
В asp.net mvc по умолчанию этого нет. Ну и гораздо легче там что-то написать. Вливайся!
+2
hannimed #
А не по-умолчанию есть? Помоему его там вообще не возможно использовать, в силу ограниченности работы с серверными контролами… кстати единственное, что удерживает меня от полного перехода на ASP.NET MVC…
+3
sse #
По-моему, M$ четко оговорили, в каком случае стоит использовать WebForms, а не лезть за ASP.NET MVC. Так что может и не стоит стремиться к полному переходу? ) Берите от обеих технологий лучшее
0
Gangsta #
Есть, можно включить если необходимо.
0
KLUBS #
можно же отключить View State на уровне Web Config. Если это сделать в начале разработки приложения, то все пойдет как по маслу
0
alemiks #
В смысле отключить ViewState вообще? В данном случае состояние представления не отключается, а переносится из скрытого поля браузера в сессию на сервере.
0
KLUBS #
Дино Эспозито в своей книге «ASP.NET 2.0. Базовый курс» (гл. 13) предлагает два варианта решения проблемы: отключение ViewState у элементов

Можно отключить View State сразу у всех элементов и страниц
+2
alemiks #
ViewState — это основа WebForms-приложений. По-моему, лучше тогда писать на classic ASP, чем ASP.NET WebForms-приложения без ViewState.
Ещё раз повторю, что затрагивается проблема переноса состояния представления из браузера на сервер, а не полное его отключение.
0
Smerig #
вьюстейт совсем не отключается. Он всегда остается. Гляньте все же в код страницы, пусть он и пустой, или с десяток символов, но он есть. Ссылок доказательства приводить не буду, но смысл вроде в том, что при создании формы всегда создается вьюстейт для формы. Да, его, типа, отключаешь, но он остается :)
0
codevar #
Чтобы не использовать ViewState т.е чтобы не было этого самого поля в разметки страницы, нужно не использовать runat=«server», следовательно отказаться от asp.net контролов. Лично я вам советую ASP MVC. Web Forms это не для искушенных, для тех кому все ровно есть ли ViewState или его нет.
+1
wchk #
Применил данный метод у себя. Посмотрел хтмл код, в результате viewstate(в hidden поле) намного уменьшился, но не исчез.
0
AigizK #
не забудь потом во время удалить ViewState из сервера.
+6
Archon #
Перестаньте применять философию PHP («хочу контролировать каждый символ, отдаваемый браузеру») к ASP.NET, и жить сразу станет проще.
0
homm #
Я как пользователь веба за то, чтобы аэспешники применяли «философию PHP». Мне нафиг не упали 80 кб мусора с каждой страницей, пусть это на сервере хранится.
0
XaocCPS #
в нормальных приложениях мусора нет
характерный пример отличного ASP.NET приложения stackoverflow.com
+2
jaffa #
Да, как они пишут в блоге, они используют ASP.NET MVC, а это значит что ни о каких ViewState речи идти не может.
0
alemiks #
Там, кстати, xss на форме «Ask a Question», а в WebForms кавычки экранируются автоматически.
0
yorick_kiev_ua #
Зашёл я на Хабр. Тут же упало 839 К… Вот если бы приехало еще 80К view state-а — это была бы катастрофа. А так еще, фух, жить можно…

Не, можно, конечно, на спичках поэкономить, но такие случаи становятся всё более редкими…
0
homm #
Не будь идиотом. Картинки можно отключить -600кб. JS и CSS кешируется -140 кб. Того 100 кб. Зачем мне еще раз вспоминать, что люби ходят в интернет не только с 10-и мегабитных каналов с соседнего квартала с дата центром.
0
Gangsta #
Забавно, но разработчики ASP.NET MVC именно так и утверждают: «хочу контролировать каждый символ, отдаваемый браузеру» :)
+1
zabr #
+4
zabr #
А в ASP.net MVC использую вот такое просто для сжатия (в Global.asax.cs ):

protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
    {
      HttpApplication app = sender as HttpApplication;
      if (app == null) return;

      string acceptEncoding = app.Request.Headers["Accept-Encoding"];
      Stream prevUncompressedStream = app.Response.Filter;

      if (!(app.Context.CurrentHandler is Page) || app.Request["HTTP_X_MICROSOFTAJAX"] != null) return;
      if (string.IsNullOrEmpty(acceptEncoding)) return;

      acceptEncoding = acceptEncoding.ToLower();

      if (acceptEncoding.Contains("deflate") || acceptEncoding == "*")
      {
        // defalte
        app.Response.Filter = new DeflateStream(prevUncompressedStream, CompressionMode.Compress);
        app.Response.AppendHeader("Content-Encoding", "deflate");
      }
      else if (acceptEncoding.Contains("gzip"))
      {
        // gzip
        app.Response.Filter = new GZipStream(prevUncompressedStream, CompressionMode.Compress);
        app.Response.AppendHeader("Content-Encoding", "gzip");
      }
    }


* This source code was highlighted with Source Code Highlighter.
0
RomanNikitin #
Это ведь легко настраивается на IIS.
или вы не имеете доступа к настройкам IIS?
+6
XaocCPS #
так и порывает продолжить заголовок статьи: «Прощай ViewState, привет MVC!»
:-)
0
feorex #
Так можно же настроить, чтобы ViewState в базе данных хранился или в файле. И памяти на сервере не будет кушать, и работает быстро, и клиент «чистенький».
0
RomanNikitin #
ViewState или SessionState?

Мне кажется, вы говорите про 2е.
0
feorex #
Да вроде и ViewState можно, например так:
www.codeproject.com/KB/viewstate/ViewStateProvider.aspx
0
AlexS #
Мы пробовали на проекте хранить viewstate в sql-сервере, но… набрав 16 гигабайт за несколько часов, отказались от этой идеи.

Насчет сессии — надо будет проверить, идея ведь неплохая! ;)
0
Smerig #
Думал, щас начнётся… Там отключили, тут отключили. А в форме нельзя отключить, поэтому всегда будет вьюстейт. Ан нет. Вон оно как :)
0
Smerig #
оказывается даже и в этом случае он присутствует. Тогда вариант такой, что отключать вьюстейт, а если он кому-то нужен, то можно его перенести на сервер.
0
linuxxspb #
можно ещё добавить в web.config:
<system.web>
  <browserCaps>
   <case>
    RequiresControlStateInSession=true
   </case>
  </browserCaps>
 </system.web>


* This source code was highlighted with Source Code Highlighter.


За счёт этого, весь ControlState, который тоже сохраняется в ViewState, будет храниться в сессии. За счёт всего этого, я добился, что у меня ViewState всего пара символов, от которых, к сожалению, уже не избавиться.
0
hannimed #
Избавиться, в Page_PreRender удалить его регекспами или простыми строковыми функциями :)
0
sse #
Не стоит. Он там не просто так, а по соображениям безопасности — чтобы ответ с клиента внезапно не расстроил сервер. Если вы отключаете эту проверку, будьте готовы к потенциальной дыре в безопасности.
0
hannimed #
Конечно не стоит, но это возможно.
0
AlexSuslin #
Я так понимаю эти пара символов и есть ключ, по которому будет искаться вьюстейт в сессии… смысл его отключать?
+2
dmomen #
Еще раз скажу — MVC рулит! Про MVC много не бывает!
0
hannimed #
Этот метод хорош, но иногда. Бывают случаи, когда ViewState спасает в то время когда сессия уже упала.
ViewState также можно распознать, если приложение работает на нескольких серверах (сессии тоже можно, но гемор настраивать).
0
AlexSuslin #
У себя на проектаххраню вьюстейт в сессии (изначально было в кеше, потом переписывлось для сессии):

  public class CachePageStatePersister: PageStatePersister
  {

    public CachePageStatePersister(Page page): base(page)
    {}

    public override void Load()
    {

      // Get the cache key from the web form data
      string key = Page.Request.Params["__VIEWSTATE_CACHEKEY"];

      // Abort if cache key is not available or valid
      if (String.IsNullOrEmpty(key) || !key.StartsWith(«VIEWSTATE_»))
        return;
//        throw new ApplicationException(«Missing vaild __VIEWSTATE_CACHEKEY»);

      Pair state = Page.Session[key] as Pair;

      // Abort if cache object is not of type Pair
      if (state == null)
        return;
//        throw new ApplicationException(«Missing vaild __VIEWSTATE_CACHEKEY»);

      // Set view state and control state
      ViewState = state.First;
      ControlState = state.Second;

    }

    public override void Save()
    {

      // No processing needed if no states available
      if (ViewState == null && ControlState == null)
        return;

      StringBuilder key = new StringBuilder();

      // Generate a unique cache key
      key.Append(«VIEWSTATE_»)
        .Append(Page.Session != null? Page.Session.SessionID: Guid.NewGuid().ToString())
        .Append("_")
        .Append(Page.Request.RawUrl)
        .Append("_")
        .Append(DateTime.Now.Ticks.ToString());

      // Save view state and control state separately
      Pair state = new Pair(ViewState, ControlState);

      // Add view state and control state to cache
//      Page.Cache.Add(
//        key.ToString(),
//        state,
//        null,
//        DateTime.Now.AddMinutes(15),
//        Cache.NoSlidingExpiration,
//        CacheItemPriority.Default,
//        null
//      );
      if(Page.Session[key.ToString()] == null)
        Page.Session.Add(key.ToString(), state);

      // Register hidden field to store cache key in
      Page.Clientscript.RegisterHiddenField("__VIEWSTATE_CACHEKEY", key.ToString());

    }

  }

* This source code was highlighted with Source Code Highlighter.
0
Nata111 #
Я почти во всех форумах вижу следующую рекомендацию:

public class LightWeightPage: Page
{
protected override PageStatePersister PageStatePersister
{
get { return new SessionPageStatePersister(this); }
}
}

И больше делать ничего не нужно.
Чем этот метод плох? У меня в одном проекте именно так сделано, глюков пока не замечено.
0
sse #
Тем, что вы пользуетесь наследованием для реализации функционала. В данном случае этот метод не так чтобы плох, потому что функционал сессии логически относится к объекту LightPage, но в случае уже существующего проекта переписывать все страницы, чтобы унаследовать их от LightPage, будет не самой хорошей идеей.
0
Silenius #
Это прекрасно! Это все замечательно! Почитал я холивар сверху и недоволен. Отпишитесь от этого блога, читайте свою хабраленту, не лезьте в чужую песочницу!

Зачем вы заходить в топик чужой технологии и оставлять первым комментом «Вот дерьмо, вы еще об этом и пишите! А вот я на своих рельсах..»? Вы вообще зачем пытаетесь сравнивать руби и дотнет? .NET — решение энтерпрайз уровня и говорить про руби в этом контексте как минимум некорректно. Понятно, что на нем и сайты делают и многим это нравится: платформа многоцелевая. Можете про джаву похоливарить, это хотя бы будет уместное сравнение.

Автор, спасибо, продолжайте! :D
0
zyb #
На заметку. При солидной нагрузке на сайт с записью ViewState в сесию, StateServer начинает перезапускаться. Пришлось вынести пока ViewState на файловую систему.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.