Бессильный pop profile asp. Конфигурирование профилей. Профили и пользовательские типы данных

Профили

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

В ASP.NET 1.x единственная практическая возможность сохранять специфичную для пользователя информацию состояла в создании собственных компонентов доступа к данным. Веб-страница может вызывать методы этого компонента доступа к данным, чтобы получать данные текущего пользователя и затем сохранять любые изменения в них. Этот подход по-прежнему имеет смысл во многих сценариях. Однако в ASP.NET 2.0 появилось другое средство, называемое профилями, которое в ASP.NET 4 осталось неизменным. Когда используются профили, ASP.NET автоматически управляет извлечением специфичных для пользователя данных, обращаясь за ними к источнику данных заднего плана (обычно - к базе данных).

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

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

Прежде чем начать применять профили, необходимо тщательно оценить их возможности. Этому посвящается несколько последующих разделов.

Производительность профилей

Назначение средства профилей ASP.NET заключается в обеспечении прозрачного способа управления специфичной для пользователя информацией без необходимости писать специальный код доступа к данным с применением классов данных ADO.NET.

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

Итак, испытывают ли профили проблемы масштабируемости? На этот вопрос не существует простого ответа. Все зависит от того, какой объем данных нужно хранить и как часто к ним необходимо обращаться. Чтобы принять взвешенное решение, понадобится узнать немного больше о том, как работают профили.

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

    При первом обращении к объекту Profile в коде ASP.NET извлекает всю информацию профиля текущего пользователя из базы данных. С этого момента информацией профиля можно манипулировать в коде без какой-либо нагрузки на базу данных (до следующей обратной отправки).

    Если в данные профиля вносятся изменения, обновление откладывается до окончания обработки страницы. В этот момент (после того как на странице произойдут события PreRender, PreRenderComplete и Unload) профиль записывается обратно в базу данных. Таким образом, множественные изменения собираются в одну операцию. Если профиль не изменяется, никакой дополнительной работы базы данных не требуется.

Таким образом, средство профилей может дважды обращаться к базе данных при каждом запросе (в сценарии чтения-записи) или же всего один раз (если данные профиля просто читаются). Средство профилей не интегрировано с кэшированием, поэтому каждый запрос к данным профиля требует соединения с базой данных. Чтение и сохранение профилей реализовано специальным модулем ProfileModule , который запускается при каждом запросе.

С точки зрения производительности профили лучше работают, когда удовлетворяются следующие условия:

Есть относительно немного страниц, которые обращаются к данным профиля.

Сохраняется небольшой объем данных.

Хуже они работают при таких условиях:

Есть много страниц, которые нуждаются в использовании информации профиля.

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

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

Как система профилей хранит данные

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

Alex ErohinЛенинский проспект 68Москва119296Россия

Другое поле указывает, где начинается и заканчивается каждое значение, с использованием формата вроде следующего:

Name:S:0:11:Street:S:11:21:City:S:32:6:ZipCode:S:38:6:Country:S:44:6

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

Например, предположим, что профиль применяется дня хранения адреса заказчика. Из-за специфического формата не получится генерировать списки заказчиков в таких приложениях, как Microsoft Word, или выполнять запросы, которые фильтруют или сортируют записи с использованием данных профиля. (Например, выполнить запрос для получения всех заказчиков, живущих в определенном городе, не получится.)

Эта проблема имеет два решения:

    Использовать специальные компоненты доступа к данным вместо профилей, чтобы сохранять данные в базе и извлекать их оттуда.

    Использовать собственный поставщик профилей, спроектированный специально для хранения информации с применением схемы базы данных.

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

Второй вариант интересен тем, что позволяет странице продолжать пользоваться моделью профилей. Фактически можно создать приложение, использующее стандартную сериализацию профилей с помощью SqlProfileProvider, а позднее переключиться на применение специального поставщика. Для такого переключения изменять код не понадобится. Нужно просто модифицировать настройки профиля в файле web.config. По мере того как веб-сайты будут шире применять профили, привлекательность специальных поставщиков профилей возрастет.

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

Профили и аутентификация

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

Сравнение профилей и специальных компонентов данных

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

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

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

Шифрование

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

Проверка достоверности

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

Кэширование

Если информация профиля используется на странице, она всегда извлекается из базы данных. Информацию из профиля нельзя хранить в памяти. Хотя можно скопировать информацию профиля в кэш, это сильно затруднит ее отслеживание.

Диагностика

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

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


Что мы ждём от профайлера? Нам нужна абсолютная (время) и относительная (проценты) статистика выполнения отдельных участков кода (если возможно, с точностью до строки). После оптимизации каких-то критичных участков кода нужно запустить профайлинг ещё раз и сравнить результаты.

Так как по роду деятельности я связан с разработкой под web, то в качестве подопытного будет выступать ASP.NET приложение (скачать):

  • public partial class Default: System.Web.UI.Page
  • protected void Page_Load(object sender, EventArgs e)
  • SampleBadMethod1();
  • SampleBadMethod2();
  • private void SampleBadMethod1()
  • for (int i = 0; i < 100; i++)
  • SampleBadSubMethod1();
  • private void SampleBadSubMethod1()
  • Thread.Sleep(10);
  • private void SampleBadMethod2()
  • for (int i = 0; i < 10000; i++)
  • SampleBadSubMethod2();
  • private void SampleBadSubMethod2()
  • Thread.Sleep(1);
  • * This source code was highlighted with Source Code Highlighter .
    Небольшое лирическое отступление. Сначала хотел выбрать реальное приложение для профайлинга. Выбор пал на Tailspin Spyworks , которое используется в качестве steb-by-step руководства. Казалось бы, руководство для новичков должно быть так отполировано, чтобы сразу заинтересовать разработчика, научить каким-то правильным вещам. И что я там увидел? Кривоватую вёрстку, смесь бизнес-логики и разметки, неоптимальные запросы к БД, select * даже если тянутся 1-2 поля… Выполнять все оптимизации 4 раза (для 4-х профайлеров) оказалось очень трудоёмко, поэтому за 3 минуты было написано используемое в тестах приложение. Если кому-то интересно, в будущих статьях можно будет разобрать по косточкам Tailspin Spyworks.

    Довольно лирики, запускаем профайлеры.

    Visual Studio Perfomance Profiler Запуск производится из VS через меню Analyze -> Launch Perfomance Wizard.
    С помощью нескольких простых шагов выбираем тип профайлинга (я выбрал Instrumentation, т.к. в CPU sampling не показывается время выполнения, только в процентах), исследуемые проекты и нужно ли профилировать запросы к БД через ADO.NET.
    После нажатия кнопки Finish запускается браузер, при этом в студии будет висеть заставка с кнопками Pause и Stop.

    После окончания загрузки страницы в браузере, нажимаем Stop и получаем результат:

    Вот как выглядит экран статистики по методам:

    Теперь нужно оптимизировать критичные участки кода SampleBadMethod1 и SampleBadMethod2. В качестве «оптимизации» сокращаем количество итераций в цикле (например, со 100 до 50 и c 10000 до 1000).
    Получаем ещё раз результат и через пункт меню Analyze->Compare Perfomance Reports сравниваем результат:

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

    Повторим те же действия с другими профайлерами.

    ANTS Profiler При создании новой сессии профайлинга появляется следующее окно:

    Здесь можно выбрать тип приложения, опции профайлинга и ввести данные приложения, например, для ASP.NET application на dev-сервере это путь до приложения, версия.net, номер порта и т.п.
    После нажатия кнопки Start Profiling запускается браузер, в ANTS Profiler в это время рисуется график загрузки процессора по оси времени.
    Нажимаем кнопку Stop Profiling и получаем результат:

    Подробно рассматривать назначение и функции областей экрана сейчас не буду (это тема отдельной статьи), кратко скажу, что в верхней части видно временную шкалу с загрузкой ЦП (или любым другим показателем, который вы можете выбрать сами), в центре дерево методов со статистикой выполнения по времени и количеству вызовов, в нижней части просмотр исходного кода методов (если код доступен).
    Здесь есть некоторая странность: мы видим, что суммарное время выполнения метода SampleBadSubMethod2 равно 14 мс, хотя внутри него задержка на 1 мс и он вызывается 10000 раз. Возможно, ANTS как-то некорректно обрабатывает метод Thread.Sleep.

    Теперь снова «оптимизируем» приложение, запускаем профайлер, получаем результат и… не можем сравнить средствами ANTS… В FAQ на сайте предлагают запустить ещё один профайлер и переключаться между ними, сравнивая результат. Ну, спасибо, что ещё сказать:)

    В бой вступает

    dotTrace При выборе File->Profile появляется окно:

    Выбираем тип приложения, задаём настройки и Run!
    После запуска профайлинга появляется небольшое окно, с помощью которого можно получить результат (Get Snapshot) или завершить профайлинг.

    После получения результата увидим следующее окно:

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

    Ну что ж, оптимизация удалась.

    И напоследок

    EQATEC Profiler Запускаем профайлер, видим окно:

    Здесь нужно выбрать путь до папки bin приложения, выбрать сборки, которые хотим исследовать на нажать Build.

    Затем запускаем свой любимый браузер и загружаем страницу приложения. Если всё нормально, то в логе появится надпись Profiled appication xxx started и станут активными кнопки «Take snapshot» и «Reset counters»

    А вот и наш snapshot:

    Видим статистику вызовов, а также представление методов в виде блоков (в нижней части). Эти блоки кликабельны, т.е. можно переходить по иерархии вызовов вниз/вверх.
    Теперь в очередной раз оптимизируем приложение и сравниваем результат:

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

    На этом всё, посмотрим на сводную таблицу возможностей представленных профайлеров

    Summary VS Profiler ANTS dotTrace EQATEC
    Показ относительных результатов выполнения методов (в %)
    Показ абсолютных результатов выполнения методов (в секундах, мс и т.п.)
    Показ числа вызовов методов
    Просмотр исходников методов
    Сравнение результатов двух замеров
    Цена > 5000$ 1) от 395$ 2) от 199$ 3) бесплатно 4)

    1) в составе VS Premium и выше
    2) зависит от редакции
    3) для open source проектов бесплатен
    4) ограничение на 10 одновременно загружаемых dll, за $ с меньшими ограничениями

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

    VS Profiler встроен в VS (правда, в premium и ultimate)
    взаимодействие с ADO.NET
    капризный, иногда профайлинг не запускается без объяснения причин
    секция appSettings должна быть в web.config, а не вынесена в отдельный файл, т.к. туда пишутся какие-то служебные настройки, а разработчики видимо не предусмотрели расположение данной секции во внешнем файле
    на большом проекте и не очень мощной машине заметно подтормаживаетANTS Profiler самая подробная информация по вызовам методов, куча счетчиков производительности
    в режиме профайла SQL думает, что кроме./SQLEXPRESS серверов больше не существует:)
    нет сравнения двух результатов замеровdotTrace больше всех понравилась документация
    в режиме просмотра дерева какая-то каша из цифр, названий методов, сборок
    не запустился в режиме IIS Application, хотя всё делал по хорошей документации.EQATEC маленький и быстрый, подходит, если не нужно смотреть производительность построчно и не нужны исходники
    данный минус - следствие плюса: меньше, чем у других конкурентов, возможностей.

    Итак, сделаю, возможно, субъективный вывод. Мой выбор - EQATEC Profiler, для многих задач оценки производительности его более чем достаточно. Если у вас есть возможность использования VS Premium или Ultimate, встроенный профайлер достаточно неплохой продукт. В этом случае необходимость в покупке других профайлеров отпадёт. Из оставшихся двух профайлеров своей мощью поражает ANTS профайлер, хотя, конечно, почему нет сравнения результатов - непонятно. У dotTrace обилие вариантов приобретения с большим количеством возможностей самого профайлера.

    Спасибо, что дочитали этот обзор! Надеюсь, он поможет вам сделать выбор.

    P.S. Предложения по подробному обзору каждого профайлера принимаются.

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

    Однако это задачка не из легких. Представьте, что вы — продавец большого магазина, в котором ежедневно бывает не одна сотня покупателей, а что если вы — сайт, который ежедневно просматривают десятки тысяч посетителей? Нужно ведь не просто запомнить каждого, но еще и сохранить все это, пока он не соизволит зайти к вам еще раз, чтобы вы смогли продемонстрировать ему свою феноменальную память. А что если за это время его вкусы изменились и придется все запоминать заново, проблемы растут как снежный ком. Чтобы автоматизировать задачу потребуется не одна таблица в базе данных, множество запросов к ним, да еще и куча кода, чтобы связать все воедино. Ну и тестирование, тестирование, тестирование. Задумаешься, а стоит ли овчинка выделки? Однако не все так плохо, с выходом ASP.NET 2.0 все проблемы отходят на второй план, потому что теперь он содержит новую встроенную возможность — профиль (Profile) пользователя.

    Профиль пользователя

    Сервис персонализации (personalization) – это новая возможность в ASP.NET, которая применялась еще в продукте Site Server. Данный сервис предоставляет готовое решение для хранения персональных данных, непосредственно характеризующих пользователя сайта, так называемого профиля пользователя (Profile). Вот несколько примеров подобных данных – это имя, фамилия, адрес электронной почты пользователя, его временная зона, а также “корзина покупателя» для пользователя сайта электронной коммерции или информация о принадлежности посетителя сайта тем или иным группам пользователей. Сейчас такие параметры обычно хранят в сессии (Session), в файлах cookie, базах данных и так далее. Независимо от места их хранения, ASP.NET 1.x мало помогает в этом случае. Приходится своими силами разрабатывать и настраивать всю инфраструктуру под соответствующее хранилище. Также самостоятельно необходимо получать эти данные и сохранять их. Коллекция Session облегчает жизнь, но она ненадежна, а хранение ее в отдельном процессе или в базе данных приводило к высоким затратам ресурсов сервера и увеличению времени отклика приложения. В сущности, сервис персонализации — это коллекция, очень похожая на сессию по своей сути. Так же, как и сессия, профиль пользователя относится к одному конкретному посетителю сайта и обеспечивает хранение информации, относящейся непосредственно к нему. Однако есть и значительные отличия, выгодно характеризующие новый сервис, среди них:

    * Строго типизированный доступ к элементам коллекции: в отличие от коллекций типа Application, Session, Context, Cache, да и всех остальных коллекций, перешедших по наследству от ASP.NET 1.x, которые осуществляли доступ к значениям своих элементов по ключу, профиль пользователя – класс HttpProfile – использует строго типизированные именованные свойства, работать с которыми гораздо удобнее. Запоминать названия и типы свойств больше не нужно, теперь это удел Intellisense, который справляется с этим гораздо лучше. Другой полезный эффект — проверка типов на этапе компиляции. Например, следующий фрагмент кода демонстрирует два варианта получения даты последней активности пользователя:
    o старый, с использованием сессии:
    DateTime LastLogin = (DateTime)Session[“LastLogin»];
    o новый, с привлечением сервиса персонализации:
    DateTime LastLogin = Profile.LastLogin;
    Заметьте, что во втором случае приведение типов не потребовалось, поскольку дата уже хранится как дата, а не упаковывается в object.
    * Выборка данных из хранилища производится только при необходимости. В отличие от класса Session данные извлекаются не при каждом запросе страницы, занимая ресурсы сервера, а только тогда, когда в них действительно есть потребность.
    * Время хранения данных не ограничено. В отличие от всех остальных коллекций, время жизни сохраненных данных в профиле бесконечно и никак не зависит ни от приложения, ни от текущей сессии. Фактически данные будут храниться вечно, если хранилище достаточно надежно и Вы сами не решите их однажды удалить оттуда.
    * Данные могут храниться как для известных (зарегистрированных и прошедших аутентификацию в системе), так и для неизвестных (анонимных, то есть еще не зарегистрированных или просто не вошедших в систему) посетителей сайта.
    * Доступ к атрибутам профиля осуществляется так же просто, как и при работе с коллекцией Session. Это возможно благодаря новому свойству Profile, доступному во всех производных от System.Web.UI.Control классах приложения. Либо во всех классах приложения через контекст веб-приложения, используя следующий синтаксис: System.Web.HttpContext.Current.Profile или ((ProfileCommon)System.Web.HttpContext.Current.Profile).MessagesPosted если нужен доступ к собственным свойствам профиля. ProfileCommon — это специальный класс, автоматически создаваемый компилятором при включенном профиле и содержащий все пользовательские свойства, добавленные через web.config.
    * Хранилище для данных расширяемо и может быть любым. По-умолчанию в поставку ASP.NET 2.0 входит один провайдер для профиля пользователя – провайдер для Microsoft SQL Server. Но возможно и создание своего собственного провайдера данных. Для этого предлагается новая модель реализации провайдеров. Она подразумевает выделение из программного интерфейса хранилища уровня работы с данными. Так достигается возможность прозрачного доступа к любым данным, независимо от типа физического хранилища – это может быть база данных и файл на жестком диске или даже выделенный сервер данных. Подобная архитектура провайдера данных используется и в других сервисах ASP.NET 2.0. Позже мы рассмотрим пример создания собственного провайдера данных и увидим, насколько это просто. Провайдер данных может быть задан статически в файле конфигурации приложения или динамически назначен в режиме выполнения.

    Давайте рассмотрим, как можно начать использовать профиль. Итак, профиль сохраняет данные о конкретном пользователе вашего сайта и позволяет затем эти получать. По умолчанию все эти данные будут сохраняться в таблицах базы данных SQL Server Express. Строка соединения по умолчанию называется «LocalSqlServer» и задана она в глобальном файле настроек machine.config. Вот как она выглядит:

    то есть база данных будет работать в режиме User Instance, название файла базы данных ASPNETDB.MDF и при первой попытке сохранения профиля база данных будет автоматически создана в подпапке App_Data корневой директории сайта.

    Отлично, оказывается все уже настроено, все что нам остается сделать — это описать нужные нам поля профиля в файле конфигурации приложения web.config. Настроим профиль на хранение имени и фамилии пользователя, а также счетчика его посещений нашего сайта:

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

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

    protected override void OnLoad(EventArgs e) { if (!IsPostBack) { tbFirstName.Text = Profile.FirstName; tbLastName.Text = Profile.LastName; lblVisits.Text = Profile.Visits.ToString(); } } protected void Update(object s, EventArgs e) { Profile.FirstName = tbFirstName.Text; Profile.LastName = tbLastName.Text; Profile.Visits += 1; } Имя:
    Фамилия:
    Кол-во посещений:

    Расширение профиля пользователя

    Профиль содержит следующие базовые параметры:

    Для расширения класса Profile используется элемент раздела элемента файла конфигурации приложения — web.config. С помощью этого элемента мы можем добавлять новые свойства к классу Profile. При компиляции страницы ASP.NET заменит класс Profile, генерируемый по умолчанию, классом построенным на основании свойств, заданных в разделе элемента. Все это произойдет автоматически, без нашего участия. Поддержка новых свойств IntelliSense-ом в Visual Studio начинается с сохранения конфигурационного файла.

    Наиболее простой вариант использования элемента — это определение нового свойства индивидуализации с единственным атрибутом — name:

    В данном случае инфраструктура ASP.NET создаст свойство с названием Nickname, имеющее тип данных по умолчанию — String, недоступное для неаутентифицированных пользователей и использующее для работы с хранилищем данных провайдер по умолчанию (это провайдер для работы с базой данных SQL Server). Кстати, имена новых свойств возможно задавать и на русском языке, а вот пробелы в них не допускаются.

    Список атрибутов, доступных в теге :

    Элемент Тип Описание
    allowAnonymous Boolean При установке в true позволяет неаутентифицированным пользователям хранить данные в системе индивидализации. При этом подразумевается, что для сайта включена возможность идентификации анонимных пользователей, то есть в конфигурационном файле присутствует элемент . Значением по умолчанию данного атрибута
    является false.
    defaultValue String Значение свойства по умолчанию. Например, для сайта может быть задана тема по умолчанию.
    name String Название свойства. Используется как ключ для доступа к значению свойства, а также
    появляется как название открытого свойства класса Profile. Является обязательным при добавлении нового свойства.
    provider String Название провайдера, используемого системой индивидуализации для взаимодействия
    с хранилищем данных. Если не задано явно, то используется значение по умолчанию
    из файла machine.config.
    readOnly Boolean Служит для указания того, что свойство является свойством только для чтения. Значение
    по умолчанию — false.
    serializeAs String Определяет метод сериализации данных. Возможны следующие значения:
    • String: значение будет сериализовано
      как строка.
    • Xml: данные сериализуются в
      XML, необходимо учесть, что будут
      сериализованы только открытые (public)
      поля класса;
    • Binary: двоичная сериализация,
      наиболее «глубокий» метод — сериализации подвергаются все поля класса, однако он
      и наиболее «дорогой», с точки зрения потребляемых ресурсов. Тип параметра должен
      быть сериализуемым;
    • ProviderSpecific: формат данных
      на усмотрение провайдера данных. Позволяет поставщику данных самому определять формат
      хранения данных. Является значением по умолчанию.
    type String Тип данных хранимого свойства. По умолчанию используется тип String, но можно задать и любой другой тип (для типа сериалиазации Binary можно задавать только
    сериализуемый тип, то есть тип, помеченный атрибутом
    Serializable, а для типа сериалиазации String класс должен иметь своего
    наследника класса TypeConverter). В последнем
    случае необходимо указать полное название класса, включая пространство имен.

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

    using System; using System.ComponentModel; using System.Xml.Serialization; public class Address { string _country; string _city; string _street; int _building; int _room; public string Country { get { return _country; } set { _country = value; } } public string City { get { return _city; } set { _city = value; } } public string Street { get { return _street; } set { _street = value; } } public int Building { get { return _building; } set { _building = value; } } public int Room { get { return _room; } set { _room = value; } } }

    Реализация класса — наследника TypeConverter, позволяющая сериализовать наш класс, как строку:

    using System; using System.ComponentModel; using System.Globalization; public class AddressTypeConverter: TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (sourceType == typeof(string)) { return true; } return base.CanConvertFrom(context, sourceType); } public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string) { string v = ((string)value).Split(new char { "|" }); Address address = new Address(); address.Country = v; address.City = v; address.Street = v; address.Building = Convert.ToInt32(v); address.Room = Convert.ToInt32(v); return address; } return base.ConvertFrom(context, culture, value); } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (destinationType == typeof(string)) { Address address = (Address)value; return address.Country + "|" + address.City + "|" + address.Street + "|" + address.Building + "|" + address.Room; } return base.ConvertTo(context, culture, value, destinationType); } }

    Хранение данных Generic-типов

    Для описания подобных данных как свойств профиля используется специальный синтаксис. Рассмотрим пример — в качестве свойства профиля пользователя нам нужно хранить свойство с названием Ints имеющее тип данных System.Collections.Generic.List. В данном случае соответствующий параметр в файле Web.config должен выглядеть так:

    То есть сначала указывается полный тип класса — коллекции, затем количество параметров с префиксом апостроф (‘1), а затем в квадратных скобках идет список типов, каждый из которых также обрамляется квадратными скобками. Для типа данных System.Collections.Generic.Dictionary запись в конфиге будет выглядеть как

    Идентификация анонимных пользователей

    Данный код будет сохранять настройки любого пользователя, даже "анонимного", то есть незарегистрированного или просто еще не выполнившего вход в систему. По умолчанию профиль сохраняется только для аутентифицированных пользователей. Для того, чтобы он начал работать и для анонимных пользователей необходимо включить модуль распознавания анонимных пользователей. За это отвечает раздел файла настроек web.config. Также необходимо для каждого параметра профиля, который должен храниться и для анонимных пользователей, установить атрибут allowAnonymous в значение "true" (по умолчанию значение этого атрибута — ложь). Если этого не сделать, то при каждой попытке установить такое свойство для анонимного пользователя будет генерироваться исключение. Рекомендуется разумно подходить к данному вопросу и не перегружать профиль анонимного пользователя бесполезными для него свойствами.

    Включение распознавания анонимных пользователй приводит к тому, чnо в цепочку обработки входящего запроса добавляется еще один модуль — AnonymousIdentificationModule. Работает он следующим образом — при поступлении запроса от анонимного пользователя модуль создает глобально уникальный идентификатор (System.Guid) и записывает его в постоянно хранимый файл cookie с именем по умолчанию.ASPXANONYMOUS. С этого момента этот Guid и будет временным именем пользователя. Поскольку файл cookie создается постоянный, то система сможет "узнавать" пользователя при каждом его последующем посещении сайта. Все параметры модуля настраиваемы и могут быть изменены в файле конфигурации, в разделе анонимной идентификации.

    Зачем это нужно? Допустим вы создаете электронный магазин посещая который пользователь может сформировать корзину интересующих его товаров не регистрируясь на сайте и не проходя авторизации. Затем он смотрит во что ему обойдутся его покупки и, если сумма его устраивает, входит в систему и завершает оформление заказа. При этом он может отложить покупку на неделю или на месяц, все это время корзина будет аккуратно хранить все выбранные им в прошлый раз товары, несмотря на то что он даже не был зарегистрирован на сайте. Для"миграции" пользователя с анонимного профиля на профиль зарегистрированного пользователя в профиле предусмотрено специальное событие — MigrateAnonymous. Оно происходит в тот момент, когда анонимный пользователь выполняет вход на сайт. Это событие может быть обработано в файле Global.asax при помощи следующего кода:

    void Profile_MigrateAnonymous(object sender, ProfileMigrateEventArgs e) { // получаем профиль анонимного пользователя по его ID ProfileCommon anonymous = Profile.GetProfile(e.AnonymousID); // Переносим данные в профиль текущего зарегистр. пользователя //if(!isMigrated) //{ Profile.FirstName = anonymous.FirstName; Profile.LastName = anonymous.LastName; Profile.SiteLook.MasterPage = anonymous.SiteLook.MasterPage; Profile.SiteLook.Theme = anonymous.SiteLook.Theme; //} // стираем cookie ".ASPXANONYMOUS" с анонимным идентификатором AnonymousIdentificationModule.ClearAnonymousIdentifier(); }

    Особенно важно выполнить удаление cookie с идентификатором анонимного пользователя. В противном случае модуль будет вызывать это событие при каждом новом запросе пользователя к сайту.

    Следует также учесть, что событие MigrateAnonymous вызывается каждый раз когда пользователь авторизуется в системе, даже если профиль пользователя уже был однажды мигрирован. Поэтому имеет смысл добавить в профиль еще один параметр "isMigrated" типа System.Boolean, который бы имел значением по умолчанию ложь, а после миграции выставлялся бы в истину. Таким образом миграцию следовало бы проводить только если этот параметр имеет значение по умолчанию (то есть профиль еще ни разу не был мигрирован).

    Группирование параметров профиля

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

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

    Profile.FirstName = "test"; Profile.SiteLook.MasterPage = "Extreme.master"; Profile.SiteLook.Theme = "Yellow";

    Работа с профилем произвольного пользователя

    До сих пор мы работали только с профилем текущего пользователя, однако в ряде случаев бывает необходимо получить профиль другого пользователя. Это с легкостью может быть выполнено при помощи метода GetProfile() класса HttpProfile, достаточно лишь передать ему имя пользователя, профиль которого мы бы хотели получить. Данный метод возвращает новый экземпляр класса HttpProfile, с которым можно работать так же, как и с профилем текущего пользователя. Возможно также и изменение свойств профиля, полученного таким образом. Однако, в этом случае, автоматического сохранения изменений не произойдет. Для того чтобы изменения вступили в силу, необходимо будет принудительно вызвать метод Save() изменяемого профиля. При использовании профиля текущего пользователя в веб-приложении не нужно вызывать этот метод для сохранения профиля, поскольку он вызывается автоматически модулем профиля при срабатывании события EndRequest.

    ProfileCommon profile = Profile.GetProfile("Nisus"); profile.FirstName = "SeaEagle"; profile.Save();

    Получить профили всех пользователей сайта также не составит большого труда:

    public ListGetProfiles() { ListProfiles = new List(); ProfileInfoCollection profilesInfos = ProfileManager.GetAllProfiles( ProfileAuthenticationOption.All); foreach (ProfileInfo profileInfo in profilesInfos) { profiles.Add(Profile.GetProfile(profileInfo.UserName)); } return profiles; }

    Перечисление ProfileAuthenticationOption, определенное в пространстве имен System.Web.Profile включает три элемента:

    * All – все пользователи, независимо от их статуса;
    * Anonymous – анонимные пользователи;
    * Authenticated – зарегистрированные пользователи.

    Доступ к профилю из внешних компонент

    Возможно вам потребуется доступ к профилю пользователя из внешних компонент, например из слоя работы с данными. Если просто написать Profile, как мы это делали на странице aspx, то код не будет компилироваться, поскольку во внешнем приложении ASP.NET не будет создавать ни класс ProfileCommon, ни уж тем более свойство Profile. Тем не менее такая возможность есть – доступ к профилю можно получить через свойство Profile контекста запроса (HttpContext). Контекст — это объект, который содержит все сведения о текущем запросе, известные ASP.NET. Одно из новых свойств контекста в ASP.NET 2.0 — это свойство Profile, содержащее ссылку на профиль текущего пользователя.

    System.Web.HttpContext context = System.Web.HttpContext.Current;
    context.Profile["FirstName"] = "Adam";
    context.Profile.Save();

    Другим, более удобным для использования вариантом, является создание класса — наследника от класса ProfileBase. Добавим в папку App_Code новый класс, описывающий нужный нам профиль:

    using System; using System.Web.Profile; using System.Collections.Generic; public class CustomProfile: ProfileBase { public string FirstName { get { return base["FirstName"] as string; } set { base["FirstName"] = value; } } public string LastName { get { return (string)base["LastName"]; } set { base["LastName"] = value; } } }

    Данный класс является наследником класса ProfileBase и лишь немного расширяет его, описывая нужные нам свойства. Всю остальныю черновую работу по общению с провайдером для получениия и сохранения значений наших свойств выполянет базовый класс. Чтобы указать инфраструктуре что в качестве базового класса для профиля следует использовать наш класс изменим описание профиля в файле конфигурации, плюс добавим новое свойство — телефон:

    Теперь как и раньше мы сможем беспрепятственно получать доступ к свойствам профиля на стрницах нашего сайта:

    Profile.FirstName = "John"; Profile.LastName = "Smith";

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

    System.Web.HttpContext context = System.Web.HttpContext.Current; string firstName = ((CustomProfile)context.Profile).FirstName;

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

    Управление профилями

    Для управления профилями существует специальный класс, ProfileManager. В нем определен ряд статических методов, позволяющих производить разнообразные вспомогательные операции. Например, удаление неиспользуемых профилей. Удаление лишних профилей играет очень важную роль, поскольку при активной работе сайта количество хранимых профилей будет расти очень быстро, особенно если используются профили для анонимных профилей. Автоматически очистка хранилища данных от неактивных профилей не производится, вы должны реализовать ее самостоятельно. Например можно выполнять очистку при каждом старте приложения

    void Application_Start(object sender, EventArgs e) { ProfileManager.DeleteInactiveProfiles(ProfileAuthenticationOption.Anonymous, DateTime.Now.AddMonths(-2)//Удалим все профили старше 2-х месяцев); }

    Также данный класс предоставляет ряд методов для получения отчетов, например метод GetAllProfiles, позволяющий получать список всех хранящихся профилей постранично.
    Настройка хранилища данных

    Microsoft SQL Server

    Давайте рассмотрим процесс создания хранилища данных, отличного от хранилища данных по умолчанию, настройки которого находятся в файле глобальной конфигурации.NET — machine.config.

    Чтобы создать новое хранилище нам потребуется создать базу данных. Делается это очень просто с помощью утилиты aspnet_regsql, которая поставляется в составе.NET Framework 2.0 и, соответственно, находится в папке "C:WINDOWSMicrosoft.NETFrameworkv2.0.50727". Утилита работает в двух режимах – в режиме мастера и в режиме командной строки. Чтобы использовать ее в графическом режиме мастера достаточно просто запустить ее на выполнение или вызвать с параметром “-W". Режим мастера весьма беден и выполняет только примитивные общие функции – выполняет в базе данных скрипты для создания или удаления всех новых возможностей, предоставляемых инфраструктурой ASP.NET (среди них Membership, Role manager, Profile, Personalization, и т.д.). Режим командной строки позволяет управлять поддержкой всех нужных Вам возможностей. Воспользуемся утилитой в режиме графического интерфейса для создания новой базы данных и установки в нее всех новых возможностей, предоставляемых ASP.NET. Данная утилита позволяет работать как с новой версией — SQL Server 2005, так и с предыдущей версией 2000, также поддерживается SQL Server Express Edition (SSE).

    Первые два окна мастера оставляем без изменений, а в третьем нам предлагают указать название инстанса SQL Server-а. Далее оставляем тип аутентификации в режиме по умолчанию – Windows и наибиваем в поле выбора базы данных имя желаемой новой базы данных – "MyOwnAspnetDB".

    Далее нажимаем два раза кнопку Next и, при небольшой доле везения, получаем сообщение о том, что скрипты были успешно инсталлированы. Остается только скопировать новую базу данных из папки Data сервера (для SSE ее расположение можно узнать в реестре – ветка HKEY_LOCAL_MACHINESOFTWAREMicrosoftMicrosoft SQL ServerSQLEXPRESSSetup) в папку App_Data нужного вам проекта, чтобы начать ее использовать в режиме UserInstanse при использовании SSE или просто указать корректную строку подключения в случае использования SQL Server полных редакций.
    Microsoft Access

    Чтобы использовать Access в качестве хранилища данных придется скачать Sample Access Provider Starter Kit с сайта http://msdn.microsoft.com/vstudio/downloads/starterkits/. Затем его нужно будет скомпилировать при помощи студии или C# Developer Express, получив в итоге ASP.NET Access Providers.dll и файл базы данных ASPNetDB.mdb, который следует положить в подпаку App_Data своего сайта, а dll-ку провайдера соотвественно в подпапку bin корневой директории сайта (если у вас нет такой папки создайте ее).
    Собственный провайдер данных

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

    Изначально сервис уже настроен на работу с SSE в режиме UserInstance. Настройки эти заданы в глобальном файле конфигурации machine.config (C:WINDOWSMicrosoft.NETFrameworkv2.0.50727CONFIGmachine.config) и могут быть переопределены в файле настроек сайта web.config. Вот как выглядят соответствующие настройки, заданные по умолчанию:

    То есть сервис настроен на использование SSE провайдера, и считается, что база данных должна называться "aspnetdb.mdf" и расположена она должна быть в папке данных ASP.NET (это подпапка App_Data корневой директории сайта). Если провайдер обнаруживает, что подобная база данных не создана, то он создает ее и все необходимые для работы объекты при первом обращении.

    Использование режима User Instance чрезвычайно удобно при разработке и тестировании приложения, однако едва ли применимо при релизной работе сайта, поэтому будьте готовы к тому, что при выставлении приложения на боевой сервер придется аттачить базу данных к инстансу SQL Server и модификации строки соединения.

    Итак, чтобы приложение смогло использовать сервис профиля с провайдером данных SSE можно ничего не менять в файле web.config, однако лучше продублировать настройки из глобального файла конфигурации, соответственно Web.config должен будет выглядеть примерно вот так:

    В случае использования Sample Access Provider Starter Kit файл настроек несколько усложнится, за счет необходимости описания нового провайдера:

    Где “ASP.NET Access Providers" в строке type="Samples.AccessProviders.AccessProfileProvider, ASP.NET Access Providers" – это название сборки (ASP.NET Access Providers.dll у вас может иметь другое название), которая получается в результате компиляции проекта Sample Access Provider Starter Kit и которую следует положить в подпапку bin корневой папки вашего сайта. Также нужно не забыть скопировать файл ASPNetDB.mdb из папки bin проекта провайдера Access в папку App_Data вашего проекта.

    Заключение

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

    Хорошо Плохо

    Конфигурирование профилей

    Поставщик SqlProfileProvider позволяет сохранять информацию профилей в базе данных SQL Server 7.0 и более поздних версий. Создавать таблицы профилей можно в любой базе данных. Однако другие детали схемы базы данных изменять нельзя, т.е. вы привязаны к определенным именам таблиц, столбцов и форматам сериализации. Для использования профилей понадобится выполнить следующие шаги:

    Создайте таблицы профиля. (Если используется SQL Server Express Edition, этот шаг выполняется автоматически.)

    Сконфигурируйте поставщика.

    Определите некоторые свойства профиля.

    Включите аутентификацию для некоторой части веб-сайта.

    Используйте свойства профилей в коде веб-страницы.

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

    Создание таблиц профилей

    Если используется версия, отличная от SQL Server Express Edition, таблицы профилей понадобится создать вручную. Для этого необходимо применить утилиту командной строки aspnet_regsql.exe - тот же инструмент, который позволяет генерировать базы данных для других средств ASP.NET, таких как состояние сеанса на базе SQL Server, членство, роли, зависимости кэша базы данных и персонализация Web Parts. Утилита aspnet_regsql.exe находится в папке C:\Windows\Microsoft.NET\Framework\[Версия].

    В случае применений SQL Server Express Edition создавать базу данных вручную не нужно. Вместо этого при первом использовании средства профилей ASP.NET создаст новую базу данных по имени aspnetdb.mdf, поместит ее в подкаталог App_Data веб-приложения и добавит таблицы профилей. Если база данных aspnetdb.mdf уже существует (поскольку она используется каким-то другим средством), то ASP.NET просто добавит к ней таблицы профилей.

    Чтобы добавить таблицы, представления и хранимые процедуры, необходимые для профилей, используется утилита aspnet_regsql.exe с опцией командной строки -A p. Единственные детали, которые должны быть указаны помимо этой - местоположение сервера (-S), имя базы данных (-d) и информация аутентификации для подключения к базе данных (используйте -U и -P для указания имени и пароля пользователя либо -E, чтобы использовать текущую учетную запись Windows). Если не указывать местоположение сервера и имя базы данных, то aspnet_regsql.exe использует экземпляр по умолчанию на текущем компьютере, а также создает базу по имени aspnetdb.

    Рассмотрим пример создания базы данных aspnetdb с именем по умолчанию на текущем компьютере за счет подключения к базе данных от имени текущей учетной записи Windows:

    Aspnet_regsql.exe -A p -E

    В таблице ниже перечислены таблицы, которые создает aspnet_regsql.exe. (Некоторые редко используемые представления не включены.)

    Таблицы базы данных, используемые для профилей Имя таблицы Описание
    aspnet_Applications

    Перечисление всех приложений, для которых существуют записи в этой базе данных. Допускается, чтобы несколько приложений ASP.NET использовали одну и ту же базу aspnetdb. В этом случае есть возможность отделить информацию профилей для каждого приложения (назначая каждому приложению отдельное имя при регистрации поставщика профилей) либо использовать одну и ту же информацию в разных приложениях совместно (назначая всем приложениям одно и то же имя)

    aspnet_Profile

    Сохраняет специфичную для пользователя информацию профиля. В поле PropertyNames перечислены имена свойств, а в поле PropertyValuesString и PropertyValuesBinary - все данные профиля, хотя придется приложить некоторые усилия, если эту информацию понадобится разбирать в других программах (не ASP,NET). Каждая запись также включает дату и время последнего обновления (LastupdatedDate)

    aspnet_SchemaVersions

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

    aspnet_Users

    Перечисляет имена пользователей с отображением их на одно из приложений в aspnet_Applications. Также фиксирует дату и время последнего запроса (LastActivityDate) и была ли запись сгенерирована автоматически для анонимного пользователя (IsAnonymous)

    Даже если имя базы данных по умолчанию (apsnetdb) не используется, должна применяться новая пустая база данных, в которой нет никаких других таблиц. Это объясняется тем, что aspnet_regsql.exe создает несколько таблиц для профилей и не стоит рисковать перемешивать их с бизнес-данными. Во всех последующих примерах предполагается использование базы данных apsnetdb.

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

    В таблице ниже перечислены наиболее важные хранимые процедуры:

    Хранимые процедуры, используемые для профилей Хранимая процедура Описание
    aspnet_Applications_CreateApplications

    Проверяет наличие указанного имени приложения в таблице aspnet_Applications и при необходимости создает запись для нее

    aspnet_CheckSchemaVersion

    Проверяет наличие поддержки специфического средства (такого как профили) в специфической версии схемы, используя для этого таблицу aspnet_SchemaVersions

    aspnet_Profile_GetProfiles

    Извлекает имя пользователя и обновляет время во всех записях таблицы aspnet_Profile для определенного веб-приложения. Не возвращает никаких действительных данных профиля

    aspnet_Profile_GetProperties

    Извлекает информацию профиля для определенного пользователя (который указан по имени). Информация никак не анализируется - эта хранимая процедура просто возвращает лежащие в основе поля (PropertyNames, PropertyValuesString, PropertyValuesBinary)

    aspnet_Profile_SetProperties

    Устанавливает информацию профиля для определенного пользователя (указанного по имени). Этой хранимой процедуре требуются поля PropertyNames, PropertyValuesString и PropertyValuesBinary. Не существует возможности обновить отдельное свойство профиля

    aspnet_Profile_GetNumberOfInactiveProfiles

    Возвращает записи профилей, которые не были востребованы в течение указанного вами времени

    aspnet_Profile_DeleteInactiveProfiles

    Удаляет записи профилей, которые не были востребованы в течение указанного вами времени

    aspnet_Users_CreateUser

    Создает новую запись в таблице aspnet_Users для определенного пользователя. Проверяет его существование (в этом случае никаких действий не предпринимается) и создает идентификатор GUID для использования в поле UserID, если таковой не указан

    aspnet_Users_DeleteUser

    Удаляет определенную запись о пользователе из таблицы aspnet_Users

    Конфигурирование поставщика

    Имея готовую базу данных, можно зарегистрировать SqlProfileProvider в файле web.config. Сначала для этого необходимо определить строку подключения к базе профилей. Затем в разделе следует удалить существующие поставщики (с помощью элемента ) и добавить новый экземпляр класса System.Web.Profile.SqlProfileProvider (с помощью элемента ). Вот как должны выглядеть конфигурационные настройки:

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

    Определение свойств профиля

    Прежде чем сохранять что-либо в таблице aspnet_Profile, оно должно быть специальным образом определено. Это делается добавлением элемента в раздел Файла web.config. Внутри элемента Должно быть предусмотрено по одному дескриптору для каждого специфичного в отношении пользователя фрагмента информации.

    Как минимум, для элемента должно быть указано имя свойства:

    ...

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

    ...

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

    Атрибуты свойств профилей Атрибут (для элемента ) Описание
    name

    Имя свойства

    type

    Полное квалифицированное имя класса, представляющего тип свойства. По умолчанию - System.String

    serializeAs

    Включает формат, используемый при сериализации (String, Binary, Xml или ProviderSpecific)

    readonly

    Этот атрибут со значением true позволяет создать свойство, которое может быть прочитано, но не изменено (попытка изменить свойство вызовет ошибку во время компиляции). По умолчанию имеет значение false

    defaultValue

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

    allowAnonymous

    Булевское значение, указывающее, должно ли данное свойство применяться со средством анонимных профилей, о котором будет сказано ниже. По умолчанию равно false

    provider

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

    Использование свойств профилей

    Поскольку свойства сохраняются в специфичных для пользователей свойствах, текущий пользователь должен быть аутентифицирован перед чтением или записью информации профиля. Можно применять систему аутентификации любого типа (Windows, с помощью форм или специальной). Понадобится только добавить правило авторизации, предотвращающее анонимный доступ к странице или папке, где планируется использование профиля. Ниже приведен пример:

    Когда упомянутые детали на месте, все готово к тому, чтобы обращаться к информации профиля, используя свойство Profile текущей страницы. При запуске приложения ASP.NET создает новый класс для представления профиля, наследуя его от ProfileBase , который служит оболочкой для коллекции настроек профиля. ASP.NET добавляет к этому классу по одному строго типизированному свойству для каждого свойства профиля, определенного в файле web.config. Эти строго типизированные свойства просто вызывают методы GetPropertyValue() и SetPropertyValue() базового класса ProfileBase для извлечения и установки соответствующих значений свойств.

    Например, если определить строковое свойство по имени FirstName, то его значение на странице можно устанавливать следующим образом:

    Profile.FirstName = "Ivan";

    Ниже представлена полная тестовая страница, которая позволяет отображать информацию профиля для текущего пользователя или устанавливать новую информацию профиля:

    Имя: Ivan
    Фамилия: Ivanov
    Дата рождения:





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

    Protected void cmdShow_Click(object sender, EventArgs e) { lbl.Text = "First Name: " + Profile.FirstName + "
    " + "Last Name: " + Profile.LastName + "
    " + "Date of Birth: " + Profile.DateOfBirth.ToString(); }

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

    Свойства профиля ведут себя подобно любой другой переменной-члену класса. Это значит, что чтение значения профиля, которое не было установлено, дает в результате значение по умолчанию (такое как пустая строка или 0).

    Щелчок на кнопке "Установить данные профиля" приводит к установке информации профиля на основе текущих значений в элементах управления:

    Protected void cmdSet_Click(object sender, EventArgs e) { Profile.FirstName = txtFirst.Text; Profile.LastName = txtLast.Text; Profile.DateOfBirth = Calendar1.SelectedDate; }

    Теперь информация профиля фиксируется в базе данных по завершении запроса страницы. Если необходимо зафиксировать часть или всю информацию раньше этого момента (возможно, несколько раз обратившись к базе данных), просто вызовите метод Profile.Save() . Как видите, средство профилей непревзойденно по своей простоте.

    Сериализация профиля

    Ранее было показано, как свойства сериализуются в одну строку. Например, при сохранении свойства FisrtName, равного "Ivan", и LastName, равного "Petrov" оба значения соединяются вместе в свойстве PropertyValuesString, экономя пространство:

    Поле PropertyNames предоставляет информацию, которая необходима для разбора значений в поле PropertyValuesString. Вот что находится в поле PropertyNames в данном примере:

    FirstName:S:0:4:LastName:S:4:6:

    Двоеточия (:) служат разделителями. Нечто интересное происходит, когда создается профиль с данными типа DataTime. Заглянув в поле PropertyValuesString, в нем можно увидеть примерно такое значение:

    2009-11-05T00:00:00

    На первый взгляд это выглядит так, как если бы данные профиля были сериализованы в виде XML, но PropertyValuesString очевидно не содержит допустимый документ (из-за текста в конце). На самом деле здесь происходит вот что: первая часть информации - DateTime - сериализована по умолчанию как XML. Следующие два свойства профиля сериализованы как обычные строки. Поле PropertyName несколько все проясняет:

    FirstName:S:0:4:LastName:S:4:6:DateOfBirth:S:10:81:

    Интересно, что формат сериализации любого свойства профиля может быть изменен простым добавлением атрибута serializeAs к его объявлению в файле web.config. Возможные значения перечислены в таблице ниже:

    Опции сериализации serializeAs Описание
    String

    Преобразует тип в строковое представление. Принимает конвертер типа, который может выполнить эту работу.

    Xml

    Преобразует тип в XML-представление, сохраняющееся в виде строки, с применением (тот же класс, что используется веб-службами)

    Binary

    Преобразует тип в соответствующее двоичное представление, понятное только.NET с использованием . Это наиболее компактная форма, но и наименее гибкая. Двоичные данные сохраняются в поле PropertyValuesBinary вместо PropertyValues

    ProviderSpecific

    Выполняет специальную сериапизацию, реализуемую пользовательским поставщиком

    Ниже показан пример изменения сериализации настроек профиля:

    Теперь при следующей установке профиля сериализованное представление в поле PropertyValuesString примет такую форму:

    Ivan Petrov 2009-11-05

    Если используется режим двоичной сериализации, значение свойства будет помещено в поле PropertyValuesBinary вместо PropertyValuesString. Единственным признаком этого смещения является наличие буквы B вместо S в поле PropertyNames.

    Все эти детали сериализации приводят к важному вопросу: что происходит, когда изменяются свойства профиля или способ их сериализации? Свойства профилей не имеют никакой поддержки версий. Тем не менее, свойства можно добавлять или удалять с относительно незначительными последствиями. Например, ProfileModule будет игнорировать свойства, которые представлены в таблице aspnet_Profile, но не определены в файле web.config. При модификации части профиля в следующий раз эти свойства будут заменены новой информацией.

    Аналогично, если профиль определяется в файле web.config, который отсутствует в сериализованной информации профиля, то ProfileModule использует только значения по умолчанию. Однако более серьезные изменения вроде переименования свойства, изменения его типа данных и тому подобного, весьма вероятно, вызовут исключение при попытке чтения информации профиля. Хуже того, поскольку сериализованный формат фиксирован, не существует простого способа миграции существующих данных профиля в его новую структуру.

    Не все типы могут быть сериализованы всеми способами. Например, классы, которые не содержат конструкторы без параметров, не могут быть сериализованы в режиме Xml. Классы, не имеющие атрибута Serializable, не могут быть сериализованы в режиме Binary. Более подробно эти исключения будут указаны, когда речь пойдет о пользовательских типах в профилях, а пока просто имейте в виду, что обойти сериализацию типов, которые поддаются сериализации, можно только при выборе определенного ее режима.

    Группы профилей

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

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

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

    LblCountry.Text = Profile.Address.Country;

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

    Профили и пользовательские типы данных

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

    Ниже показана сокращенная версия класса Address, связывающего вместе всю информацию, которая была представлена в предыдущем примере. Для краткости здесь используются автоматические свойства (которые сохраняют данные в автоматически сгенерированных приватных полях):

    Public class Address { public string Name { get; set; } public string Street { get; set; } public string City { get; set; } public string ZipCode { get; set; } public string Country { get; set; } public Address(string name, string street, string city, int zipCode, string country) { Name = name; Street = street; City = city; ZipCode = zipCode.ToString(); Country = country; } public Address() { } }

    Этот класс можно поместить в каталог App_Code (или скомпилировать в DLL-сборку в каталоге Bin). Последний шаг - добавление свойства, которое его использует:

    После этого классом можно манипулировать в коде:

    Profile.Address = new Address(txtFirst.Text, "Ленинский проспект 68", "Moscow", 119296, "Russia");

    Сериализация пользовательских типов

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

    Ivan Ленинский проспект 68 Moscow 119296 Russia

    Это XML-представление можно изменить за счет добавления к общедоступным свойствам класса атрибутов из пространства имен System.Xml.Serialization. Например, можно использовать XmlElement для изменения имени элемента XML, применяемого для хранения свойства, XmlAttribute - для того, чтобы обеспечить сохранение свойства в виде атрибута, а не элемента XML, и XmlIgnore - для предотвращения сериализации значения свойства.

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

    Если вы решите использовать двоичную сериализацию вместо XmlSerialization, то.NET применит совершенно другой подход:

    В этом случае ProfileModule обратится за помощью к BinaryFormatter. Класс BinaryFormatter может сериализовать общедоступное и приватное содержимое любого класса, если этот класс снабжен атрибутом Serializable. (При этом все классы, от которых он унаследован, и классы, на которые он ссылается, также должны быть сериализуемыми.)

    И, наконец, можно использовать строковую сериализацию:

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

    Автоматическое сохранение

    Модуль ProfileModule, сохраняющий информацию профилей, не имеет возможности обнаруживать изменения в сложных типах данных (не относящихся к строкам, простым числовым типам, булевским значениям и т.д.). Это значит, что если профиль включает в себя сложные типы данных, то ProfileModule будет сохранять информацию профиля в конце каждого запроса, который обращается к объекту Profile.

    Очевидно, что такой подход влечет за собой излишние накладные расходы. Для оптимизации производительности при работе со сложными типами доступно несколько вариантов. Один из них - сделать соответствующее свойство профиля доступным только для чтения (если известно, что оно никогда не будет изменяться). Другой подход состоит в полном отключении автоматического сохранения за счет добавления атрибута automaticSaveEnabled к элементу И установки его в false, как показано ниже:

    ...

    При таком подходе на вас возлагается ответственность за вызов Profile.Save() для явной фиксации изменений. В общем случае этот подход наиболее удобен, поскольку охватить все места в коде, где выполняется модификация профиля, довольно просто. Просто поместите в конце вызов Profile.Save():

    Protected void cmdSet_Click(object sender, EventArgs e) { Profile.FirstName = txtFirst.Text; Profile.LastName = txtLast.Text; Profile.DateOfBirth = Calendar1.SelectedDate; Profile.Address = new Address(txtFirst.Text, "Ленинский проспект 68", "Moscow", 119296, "Russia"); Profile.Save(); }

    И последний вариант предусматривает обработку события ProfileModule.ProfileAutoSaving в файле global.asax. В этом месте можно проверить, действительно ли необходимо сохранение, и отменить его, если оно не нужно.

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

    Например, рассмотрим тестовую страницу, показанную ниже, которая позволяет извлекать и модифицировать информацию адреса:

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

    Protected void txt_TextChanged(object sender, EventArgs e) { Context.Items["AddressDirtyFlag"] = true; }

    Свойство Page.Context предоставляет объект HttpContext. Коллекция HttpContext.Items является удобным местом для временного хранения данных, которые должны быть использованы позднее во время той же самой обратной отправки. Для получения аналогичного эффекта можно использовать состояние представления и состояние сеанса, однако эти подходы предполагают более длительное хранение.

    Имейте в виду, что значение, сохраняемое таким способом, остается только на протяжении текущего запроса. В данном примере это не проблема, потому что пользователь имеет только два выбора после внесения изменений - отклонить его (щелкнув на кнопке "Получить"), или сохранить (щелкнув на кнопке "Сохранить"). Однако если создается страница, на которой пользователь может вносить изменения в виде нескольких шагов, и применять их позднее, то придется немного потрудиться, чтобы реализовать логику сопровождения флага. Помещение флага в другое место, такое как состояние сеанса или состояние представления, работать не будет, т.к. ни то, ни другое не доступно, когда инициируется событие AutoSaving в файле global.asax.

    В прошлом году мне пришлось отсобеседовать около 10-15 кандидатов на должность веб-программиста на ASP.NET средней квалификации. В качестве вопросов «на засыпку», или «со звёздочкой», я просил рассказать, что происходит с HTTP-запросом от момента его поступления на 80-й порт сервера до передачи управления коду aspx-страницы. Статистика была удручающей: ни один из кандидатов не смог выдать хоть что-нибудь внятное. И этому есть своё объяснение: ни в MSDN с technet, ни на специализированном ресурсе iis.net, ни в книгах a-la «ASP.NET для профессионалов», ни в блогах данной теме не уделяется должного внимания – информацию приходится собирать чуть ли не по крупицам. Я даже знаю людей, которые решили написать свой собственный веб-сервер (Игорь, Георгий, привет!), чтобы не разбираться в работе IIS. Единственная толковая статья – «Introduction to IIS Architectures » Риган Темплин (Reagan Templin). Но и она остаётся на периферии интересов аспнетчиков.

    Хотя мне лично уже не так интересны чисто технические вопросы, я решил собрать в кучу свой накопленный опыт, раскопать на просторах Сети любопытные детали и передать сие сакральное знание массам, пока оно ещё не устарело. Сразу оговорюсь, что статья ориентирована в большей степени на IIS 7.x, иногда будут ответвления про 6-ку. С 8-й версией в работе не сталкивался, поэтому решил обойти её в этой статье стороной. Но, уверен, читатель без труда разберётся с восьмёркой, освоив изложенный ниже материал.







    1. Общий план Итак, начнём с конца, а потом рассмотрим отдельные аспекты чуть более пристально.
    В англоязычной литературе процесс обработки запроса в IIS называется «request processing pipeline» - что-то вроде «конвейера обработки запроса». В общих чертах он представлен на рисунке ниже для http-запроса.

    Рис. 1. HTTP request processing pipeline (IIS 7.x).

    Таким образом, http-запрос проходит по «сборочной ленте конвейера» через следующее:

    1. Браузер обращается к веб-серверу по определённому URL, на стороне сервера запрос перехватывает драйвер HTTP.SYS .
    2. HTTP.SYS стучится к WAS для получения информации из хранилища конфигурации.
    3. Служба WAS запрашивает конфигурацию из хранилища - из файла в папке IIS (applicationHost.config).
    4. Поскольку данный запрос получен по протоколу HTTP конфигурационную информацию получает служба W3SVC (она же WWW Service на картинке), эта информация содержит в себе данные о пуле приложений (application pool) и прочих параметрах сайта.
    5. Служба W3SVC использует эту информацию для кофигурации HTTP.SYS .
    6. Служба WAS запускает процесс W3WP.exe для пула приложений, если он ещё не был запущен.
    7. В процессе W3WP.exe работает приложение веб-сайта, которое, собственно, формирует и возвращает ответ драйверу HTTP.SYS .
    8. HTTP.SYS отправляет ответ браузеру.

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

    2. Крупный план Теперь остановимся чуть поподробнее на каждом из упомянутых компонентов.2.1. HTTP.SYS На транспортном уровне IIS использует прослушивателей протоколов (protocol listeners), которые располагаются поверх стека TCP/IP. Наиболее интересный нам такой компонент – это системный драйвер HTTP.sys, который встроен в ядро ОС и работает с протоколами HTTP и HTTPS, регистрирующийся самостоятельно на прослушку всех портов, на которые будут приходить запросы к сайтам в IIS.

    Встроенный в ядро HTTP.sys стал нововведением в IIS 6, заместив собой Windows Socket API – компонент перехвата HTTP- и HTTPS-запросов на пользовательском уровне в IIS более ранних версий. Вероятно, интеграция драйвера в ядро является той самой причиной, по которой версия IIS жёстко привязана к версии Windows.

    Драйвер принимает все входящие запросы и перенаправляет их в нужный пул приложений. Если по какой-то причине рабочий процесс, в коем хостится требуемый пул, остановлен (сбой, таймаут простоя, смена конфигурации и т.п.) или ещё запускается, то HTTP.sys сохраняет входящие запросы в специально отведённой для каждого пула очереди. Таким образом, запросы пользователей никуда не пропадают, и они вообще не замечают каких-то перебоев в работе сайтов под управлением IIS.

    Ещё HTTP.sys умеет кешировать ответы (более подробно - Instances in which HTTP.sys does not cache content), поэтому некоторые запросы обрабатываются без передачи на уровень приложения, а также проводит первичный разбор URI запроса и его валидацию в соответствии с RFC 2396 (кое-что можно почерпнуть отсюда - Use of special characters like "%" ‘.’ and ‘:’ in an IIS URL) и журналирование запросов/ответов.

    Некоторые настройки HTTP.sys вынесены в системный реестр Windows (более подробно - Http.sys registry settings for Windows). Кстати, там же – в реестре – можно подсмотреть обычное место прописки нашего гражданина: %SystemRoot%\system32\drivers\http.sys.

    Признаться, в процессе написания данной статьи я сам открыл для себя некоторые детали. Например, кэширование ответов на уровне драйвера HTTP.sys. Это помогло мне объяснить один случай странного, как мне тогда казалось, феномена в поведении IIS. Маркетологи выложили на сайт swf-открытку перед очередным праздником, но потом им что-то не понравилось в названии файла и они его переименовали. Однако сайт продолжал выдавать открытку по старому URL и даже очистка браузерного кэша не помогала. Тут уже подключился я, но ни перезапуск веб-сайта и всего пула приложений, ни обращение к сайту в обход корпоративного прокси-сервера не дали ожидаемого результата. Но теперь-то мы знаем, кто виноват. 2.2. World Wide Web Publishing Service (W3SVC) Данная служба (сокращённо именуемя в спецификациях WWW service) была представлена в IIS 6 в качестве отдельного компонента для работы с протоколами HTTP/HTTPS и управления рабочими процессами приложений и выполняла следующие функции:
    • Администрирование драйвера HTTP.sys.
    • Управление рабочими процессами.
    • Мониторинг показателей производительности веб-сайтов.
    Эта служба функционирует в Windows Server 2003 в контексте процесса Svchost.exe (настройки можно посмотреть в реестре HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3Svc ) в отличие от всех остальных служб IIS, которые исполняются в контексте процесса Inetinfo.exe, и реализована в Iisw3adm.dll.

    В IIS 7.x функция управления процессами была вынесена в отдельную службу – WAS (см. п.2.3) в целях универсализации архитектуры. Теперь WWW-служба стала по своей сути одним из адаптеров, специализируясь на протоколах HTTP/HTTPS – работа поверх драйвера HTTP.sys. Однако WWW-служба остаётся краеугольным компонентом IIS, поэтому её настройка отличается от настройки адаптеров к другим протоколам (чуть подобнее здесь); она функционирует в том же рабочем процессе, что и WAS, и реализована в той же самой библиотеке (рис. 2).


    Рис.2. Рабочий процесс со службами W3SVC и WAS.

    Раз уж зашла речь об адаптерах к прослушивателям протоколов (protocol listener adpater), то давайте чуть задержимся и посмотрим, какие они бывают. В принципе IIS 7.x можно настроить для обработки запросов по любым протоколам помимо типовых HTTP и FTP, например, POP3, SMTP, Gopher. Вы даже вольны придумать свой протокол для своей веб- или WCF-службы и реализовать для него все нужные компоненты, если не жалко своего времени. Скорее всего, адаптеры и прослушиватели для наиболее распространённых протоколов доступны для свободного и коммерческого скачивания – этого я не проверял. Но прежде всего стоить обратить внимание на стандартные службы (рис. 3), поставляемые с.NET Framework и интегрированные с IIS:

    • NetTcpActivator для протокола TCP;
    • NetPipeActivator для Named Pipes;
    • NetMsmqActivator для Message Queuing (ака MSMQ).


    Рис. 3. Перечень стандартных не-HTTP-адаптеров в оснастке Служб Windows.

    Но всё-таки наиболее важным для нас адаптером является именно WWW-служба, т.ч. остановимся чуть подробнее на двух оставшихся от IIS 6 функциях.

    Администрирование и конфигурирование HTTP(S). В момент обновления конфигурации веб-сайтов, служба WAS передаёт эту информацию WWW-службе, а та уже, в свою очередь, настраивает HTTP.sys на прослушку конкретных портов, разбор IP и заголовка запрашиваемого сайта и, возможно, других параметров драйвера. В обратную сторону W3SVC обращается к WAS, когда в очередь запросов в HTTP.sys поступает новый, – для получения рабочего процесса-обработчика данного запроса.

    Отслеживание показателей производительности. WWW-служба ведёт счётчики производительности, используя для этого драйвер HTTP.sys, и предоставляет их показатели веб-сайтами и кэшу IIS. Более подробной информации по этому вопросу мне найти не удалось.

    2.3. Windows Process Activation Service (WAS) Итак, WWW-служба в IIS 7.x, как и в IIS 6, продолжает выполнять задачи по администрированию HTTP.sys и управлению показателями производительности веб-сайтов. А вот задача управления рабочими процессами вынесена в отдельную службу – WAS. Она запускается системой в единственном экземпляре, считывает конфигурацию из файла %SystemRoot%\System32\inetsrv\Config\ApplicationHost.config и настраивает через соответствующие адаптеры прослушивателей протоколов в соответствии с указанной в нём информации. Напомним, что для протоколов HTTP/HTTPS адаптером является служба W3SVC, а прослушивателем – драйвер HTTP.sys. При перехвате прослушивателем запроса он через свой адаптер обращается к службе WAS для получения рабочего процесса приложения, которому будет передан запрос для обработки и формирования ответа клиенту.

    При активации нужного для обработки пользовательского запроса приложения задействуются следующие компоненты:

    • Адаптеры прослушивателей (Listener adapters) – специальные службы Windows, работающие с конкретным протоколом и взаимодействующие с WAS для направления запросов к правильному рабочему процессу.
    • Собственно WAS. Она ответственна за создание рабочих процессов и управление их временем жизни.
    • Исполняемый файл w3wp.exe – шаблон рабочего процесса.
    • Менеджер приложений управляет созданием и утилизацией доменов приложений (application domains), которые хостятся внутри рабочего процесса.
    • Обработчики протоколов – протоколозависимые компоненты внутри рабочего процесса, ответственные за обмен данными между конкретным адаптером и рабочим процессом. Есть 2 типа обработчиков протоколов: у процесса (process protocol handler - PPH) и у домена приложения (AppDomain protocol handlers - ADPH).
    Ниже на рисунке представлен пример схемы компонентов внутри некоего экземпляра рабочего процесса приложения. Когда служба WAS запускает рабочий процесс, она загружает в него согласно конфигурации приложения требуемые обработчики протоколов процессов (PPH) и посредством менеджера приложений создаёт внутри рабочего процесса домен приложения, в котором будет хоститься приложение. Менеджер приложений загружает код приложения в домен приложения и требуемые обработчики протоколов уровня приложения (ADPH) для обработки сообщений по соответствующим сетевым протоколам.


    Рис. 4. Компоненты w3wp.exe для взаимодействия с внешними компонентами.

    Как отмечалось выше, .NET Framework несёт в себе реализацию компонент для протоколов HTTP/HTTPS (наш любимый ASP.NET), net.tcp, net.pipe и MSMQ. Стеки протоколов HTTP/HTTPS и FTP всё-таки более тесно интегрированы в IIS и ОС, поэтому настройку для нового протокола лучше продемонстрировать на примере менее популярных дотнетовских протоколов. Итак, после установки фреймворка в файле конфигурации IIS ApplicationHost.config появляется записи:

    А соответствующие компоненты PPH и ADPH настраиваются в дотнетовском machine.config:


    
    Top