<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-329418352356606474</id><updated>2012-01-28T17:07:21.021+02:00</updated><category term='LINQ'/><category term='Visual Studio'/><category term='Usability'/><category term='MS SQL 2008'/><category term='jQuery'/><category term='MVVM'/><category term='SQL'/><category term='Binding'/><category term='Web Services'/><category term='AJAX'/><category term='UI'/><category term='Web Performance'/><category term='Caching'/><category term='IIS'/><category term='ASP.NET MVC 3'/><category term='ASP.NET'/><category term='Script Combining'/><category term='ScriptManager'/><category term='Knockout'/><category term='HTML'/><category term='SSRS'/><category term='Bulk Operations'/><category term='Expression Trees'/><category term='Debug'/><category term='JavaScript'/><category term='WPF'/><category term='хобби'/><title type='text'>Vitaly Ivanov's blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>25</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-4096607363670225962</id><published>2012-01-23T16:08:00.003+02:00</published><updated>2012-01-23T16:08:44.950+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MVVM'/><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Knockout'/><category scheme='http://www.blogger.com/atom/ns#' term='UI'/><category scheme='http://www.blogger.com/atom/ns#' term='HTML'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>Цепляем jQuery Datepicker c помощью кастомного биндинга на KnockoutJS</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Как я говорил в &lt;a href="http://delmadman.blogspot.com/2012/01/javascript-c-knockoutjs.html"&gt;предыдущей статье&lt;/a&gt; сегодня мы создадим свою "команду" ну или правильному &lt;a href="http://knockoutjs.com/documentation/custom-bindings.html" target="_blank"&gt;custom binding&lt;/a&gt; (кастомный биндинг).&lt;br /&gt;Что это такое ?&lt;br /&gt;В knockout для того, чтобы с вашим элементом происходили какие-то махинации: скрыть, сделать неактивным, добавить стиль, присвоить значение и тп существуют команды, некоторые из которых вы уже видели в примерах - value, text, visible, ckecked, enable, options. Эти команды предопределены в фреймворке и предоставляют базовый и достаточный функционал для реализации ваших нужд.&lt;br /&gt;Иногда бывают случаи когда код выглядит когда их вроде бы не хватает. Например, обавим в наши фильтры поддержку типа &lt;b&gt;Date&lt;/b&gt; и когда пользователь выбрал поле, например &lt;b&gt;Issue Date&lt;/b&gt;, установил курсор чтоб ввести значение, я хочу чтоб он не вводил его руками, а выбрал дату через &lt;a href="http://jqueryui.com/demos/datepicker/" target="_blank"&gt;jQuery datepicker&lt;/a&gt;. Естественно я хочу чтоб выбор даты срабатывал только для Date типа фильров, а для других Number и String оставался просто полем ввода.&lt;br /&gt;Без своей кастомной команды, нам бы пришлось подписаться через subscribe на &lt;span class="js-property"&gt;ColumnObj и смотреть при выборе поля из выпадающего списка его тип. Если это дата - применить datepicker, если нет - удалить datepicker. Если мы в будущем захотим добавить еще какую-то логику, то пойдут кастыли и будет все в куче.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="js-property"&gt;Тут нам как раз и поможет кастомный биндинг (команда). В итоге наш шаблон будет выглядеть так:&lt;/span&gt;&lt;br /&gt;&lt;pre class="brush: html; gutter: false;"&gt; &lt;br /&gt;    &amp;lt;input data-bind="value: Value, datePicker: Type" type="text" /&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Вся логика для выбора даты будет вынесена в команду &lt;b&gt;datePicker&lt;/b&gt;. Собственно код:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: javascript: gutter: false;"&gt;ko.bindingHandlers.datePicker = {&lt;br /&gt;    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {&lt;br /&gt;        var type = ko.utils.unwrapObservable(valueAccessor());&lt;br /&gt;&lt;br /&gt;        if (type == 'Date') {&lt;br /&gt;            $(element).datepicker();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;    },&lt;br /&gt;    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {&lt;br /&gt;        var type = ko.utils.unwrapObservable(valueAccessor());&lt;br /&gt;&lt;br /&gt;        if (type == 'Date') {&lt;br /&gt;            $(element).datepicker();&lt;br /&gt;        }&lt;br /&gt;        else {&lt;br /&gt;            $(element).datepicker("destroy");&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Я не буду описывать все опции в деталях, о них выможете прочитать &lt;a href="http://knockoutjs.com/documentation/custom-bindings.html" target="_blank"&gt;здесь&lt;/a&gt;.&lt;br /&gt;Рассмотром, что и как.&amp;nbsp; Как мы видим для того чтобы создать команду, мы создаем объект с соответствующим называнием команды в &lt;b&gt;ko.bindingHandlers&lt;/b&gt;. У нашего объекта должно быть две функции.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;init &lt;/b&gt;- вызывается, когда knockout доходит до места в html, где установленна наша команда.&lt;br /&gt;&lt;b&gt;update &lt;/b&gt;- вызывается, когда изменится значение поля за которым мы следим с помощью нащей команды.&lt;br /&gt;В нашем случае, &lt;b&gt;data-bind="dataPicker: Type, &lt;span class="xml-attribute"&gt;value: Value&lt;/span&gt;&lt;/b&gt;&lt;b&gt;"&lt;/b&gt; значит - следить за полем &lt;b&gt;Type&lt;/b&gt; и если оно изменится вызвать команду &lt;b&gt;datePicker&lt;/b&gt;. Она вызовится при инициализации и после последющих обновлений.&lt;br /&gt;Что происходит в обработчиках?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;valueAccessor()&lt;/b&gt; - возвращает поле, к которому привязана команд. Это всегда будет функция. т.к. все поля объявляются через ko.obsorvable или ko.computed и они возврашают функции. &lt;br /&gt;&lt;b&gt;ko.utils.unwrapObservable&lt;/b&gt; - это функция, которая вычисляет значение поля на такущий момент, когда вызывается эта строка. &lt;br /&gt;Дальше, я думаю, все понятно. Параметры, которые передаютися в init и update очевидны, кроме &lt;b&gt;&lt;code&gt;allBindingsAccessor&lt;/code&gt;, &lt;/b&gt;но и пользоваться им вы врядли будете.&lt;br /&gt;&lt;br /&gt;Ниже рабочий пример. Я добавил тип Date в Conditions, саму команду datePicker и поле Issue Date.&lt;br /&gt;&lt;iframe allowfullscreen="allowfullscreen" frameborder="0" src="http://jsfiddle.net/ivanov_vitaly/75fA3/9/embedded/" style="height: 300px; width: 100%;"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-4096607363670225962?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/4096607363670225962/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2012/01/jquery-datepicker-c-knockoutjs.html#comment-form' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/4096607363670225962'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/4096607363670225962'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2012/01/jquery-datepicker-c-knockoutjs.html' title='Цепляем jQuery Datepicker c помощью кастомного биндинга на KnockoutJS'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-3057025627880993211</id><published>2012-01-13T19:06:00.001+02:00</published><updated>2012-01-23T14:56:27.291+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MVVM'/><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Knockout'/><category scheme='http://www.blogger.com/atom/ns#' term='UI'/><category scheme='http://www.blogger.com/atom/ns#' term='HTML'/><title type='text'>Динамические фильтры на Javascript c помощью KnockoutJS</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Сегодня я покажу практический пример, как можно применить &lt;a href="http://knockoutjs.com/" target="_blank"&gt;KnockoutJS&lt;/a&gt; в реальном проекте и сделаем мы динамические фильтры для поиска задач, багов, тасков и тп по разработчику, названию таска и описанию. Возможно вы такие уже видели в TFS для генерации отчета или других системах, где можно добавлять любые фильтры и условия для поиска чего-либо. В итоге выглядит это так:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;iframe allowfullscreen="allowfullscreen" frameborder="0" src="http://jsfiddle.net/ivanov_vitaly/vWg97/embedded/result,js,html/presentation/" style="height: 300px; width: 100%;"&gt;&lt;/iframe&gt;&lt;br /&gt;В конце мы добавим валидацию, чтоб нельзя было добавлять фильтр, если текущий пустой, а в следующей статье я напишу о том как можно писать плагины и свои команды в knockout и добавит фильтр по дате.&lt;br /&gt;&lt;br /&gt;Приступим!&lt;br /&gt;&lt;br /&gt;Сначала&amp;nbsp; определимся, что будет представлять из себя сам фильтр:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Column &lt;/b&gt;- колонка по кторой фильтруем или ищем&lt;br /&gt;&lt;b&gt;Condition &lt;/b&gt;- оператор для фильтра =, != и тп&lt;br /&gt;&lt;b&gt;Value &lt;/b&gt;- значение фильтра&lt;br /&gt;&lt;b&gt;Type &lt;/b&gt;- тип. Пока что у нас будет только String, Number потом расширим&lt;br /&gt;&lt;b&gt;NextFilterBinaryCondition &lt;/b&gt;- это условие с которым будет выполняться следующий фильтр "И", "ИЛИ". Например Developer = "Vitaly" and Issue = "write post" or Issue Description = "knockout".&lt;br /&gt;&lt;br /&gt;Фильтры у нас будуте создаваться по названию поля по которому мы фильтруем. Колонки у нас хранятся как объекты. При создании фильтра мы сохраним название поля и найдем в списке объект колонку с помощью вспомогательной функции findFilterColumn и будем хранить обьъект колонка в поле ColumnObj.&amp;nbsp; &lt;br /&gt;Если поле ColumnObj изменится, мы очищаем значение фильтра. Чтоб следить за изменениями поля ColumnObj мы подписуемся функцией &lt;b&gt;subscribe&lt;/b&gt;.&lt;br /&gt;Функцию-оброботчик, которыю мы передаем в &lt;b&gt;subscribe, &lt;/b&gt;будет вызываться всегда, когда значение поля, на которое мы подписываемся, изменится.&lt;br /&gt;Все поля, кроме &lt;b&gt;ColumnObj, Condition, Value, NextFilterBinaryCondition&lt;/b&gt;&amp;nbsp; зависымые и значения мы не присваиваем им явно. Значения для этих полей пересчитываются при любом изменения одного из обычных полей. Это одна из прелестей knockout! Если смотреть сверху вниз то экосистема knockout будет вызывать обработчики в следующем порядке:&lt;br /&gt;1. Присвоить значение в ColumnObj&lt;br /&gt;2. Определеить поле Column как зависимое и попробовать просчитать.&lt;br /&gt;3. Присвоить значение полю Condition.&lt;br /&gt;4. Пересчитать Column&lt;br /&gt;5. Присвоить значение полю NextFilterBinaryCondition.&lt;br /&gt;6. Пересчитать Column.&lt;br /&gt;7. Определеить поле Type как зависимое и просчитать.&lt;br /&gt;8. Пересчитать Column. &lt;br /&gt;9.&amp;nbsp;Определеить поле AvailableConditions как зависимое и просчитать.&lt;br /&gt;10. Пересчитать Type.&lt;br /&gt;11. Пересчитать Column.&lt;br /&gt;12. Установить значние поля Value и пересчитать Column, Type, AvailableConditions.&lt;br /&gt;&lt;br /&gt;Все поля устанавливаются и пересчитываются по порядку в котором они были объявлены.&lt;br /&gt;&lt;br /&gt;Теперь рассмотрит класс, который управляет всей бизнес логикой, хранит фильтры и он является и называется &lt;b&gt;ViewModel&lt;/b&gt;&lt;br /&gt;&lt;iframe allowfullscreen="allowfullscreen" frameborder="0" src="http://jsfiddle.net/ivanov_vitaly/Bna2w/embedded/js/presentation/" style="height: 300px; width: 100%;"&gt;&lt;/iframe&gt;&lt;br /&gt;Объект &lt;b&gt;Conditions&lt;/b&gt; содержит список типов фильтров и соответсвенно операций, условий, которые мы поодерживаем для этих типов данных. Также есть вспомогательная функция для поиска доступных условий по названию типа.&lt;br /&gt;&lt;br /&gt;Например найти все доступные условия для фильтрации по типо 'Number'. Этой функцией мы будем пользоваться когда после выбора поля в первом списке, нам нужно будет отобразаить доступные условия во втором выпадающем списке.&lt;br /&gt;&lt;br /&gt;Небольшое ТЗ. У нас должны выполнятся следующие требования:&lt;br /&gt;- хранить фильтры&lt;br /&gt;- добавлять фильтры&lt;br /&gt;- удалять фильтры&lt;br /&gt;- посылать все фильтры на сервер&lt;br /&gt;&lt;br /&gt;При создании ViewModel мы передаем в него список колонок, которы объявлены выше. В конструкторе мы создаем массив фильтров и сразу добавляем один пустой фильтр. Если этого не сделать, то у нас на экране будет одна кнопка. Как только мы добавляем пустой фильтр, экосистема knockout трекает это, уведомляет всех подписчиков на изменения, если есть такие, и отображает пустой добавленные фильтр по шаблону и определенной для него бизнес логике.&lt;br /&gt;&lt;br /&gt;Функция &lt;b&gt;addFilter&lt;/b&gt; принимает объект фильтр, напротив которого мы клацнули "+", находит этот фильтр в списке уже существующих фильтров и добавляет после него новый пустой.&lt;br /&gt;В данном случае у нас нет проверки, заполнены ли все фильтры или нет. Сейчас мы можем нажимать "+" бесконечно и у нас будет куча фильтров. Мы это исправим потом.&lt;br /&gt;&lt;br /&gt;Функция &lt;b&gt;removeFilter&lt;/b&gt; принимает объект фильтры который надо удалить, удаляет его из списка фильтров и если это был последний фильтр мы добавляем пустой. Зачем? Если мы этого не сделаем, то мы не сможем добавить фильтры, т.к. "+" отображается на строке с фильтром и без него у нас останется только кнопка &lt;b&gt;submit&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Функция &lt;b&gt;postFilters&lt;/b&gt; показывает сообщение с сериализованными фильтрами в JSON, которые надо послать на сервер для обработки. В knockout есть много утилитных функций о которых мы можете почитать подробней &lt;a href="http://www.knockmeout.net/2011/04/utility-functions-in-knockoutjs.html" target="_blank"&gt;здесь&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Функция &lt;b&gt;toJS&lt;/b&gt;, создает новый js объект, который не связан больше с knockout, это просто js объект. Все свойства в нем будут вычеслены с теми значениями, которые были на момент вызова функции toJS.&lt;br /&gt;&lt;br /&gt;Когда мы сериализовали фильтры в js objects, то там будут поля, которые нам нужны и которые были нужны только для knockout, например &lt;b&gt;ColumnObj, AvailableConditions&lt;/b&gt;. Эти поля не несут нам никакой информации на сервере, поэтому мы их смело удаляем и на сервер пойдет меньше данных. Функция toJSON сериализует js класс в JSON строку, которую мы и показываем.&lt;br /&gt;&lt;br /&gt;Переходим к рассмотрению шаблона html:&lt;br /&gt;&lt;br /&gt;Фильтры отображаются по шаблону в данном случае шаблон, это li и все что внутри него.&lt;br /&gt;В предыдущих версия knockout обязательно надо было подключать jQuery Teample. В последней версии этого делать больше не нужно, теперь в фреймворке есть свой встроеный движок для шаблонов со своими плюшками. О всех новшествах новой версии 2.0 можно прочитать на блоге самого разработчика &lt;a href="http://blog.stevensanderson.com/2011/12/21/knockout-2-0-0-released/" target="_blank"&gt;здесь.&lt;/a&gt;&lt;br /&gt;Из нового&amp;nbsp; я использую переменную &lt;b&gt;$root&lt;/b&gt;, которая ссылается на ViewModel класс. Если этого не сделать, то, например, поле &lt;b&gt;columns&lt;/b&gt; будет искаться в классе &lt;b&gt;Filter&lt;/b&gt;. Команда &lt;b&gt;foreach&lt;/b&gt; пробегает по всем объектам коллекции &lt;b&gt;filters&lt;/b&gt; и применяет шаблон для каждого объекта Filter. &lt;u&gt;Шаблон выполняется с контекстом объекта&lt;/u&gt;, который передается в шаблон (Filter).&lt;br /&gt;Переменная &lt;b&gt;$data &lt;/b&gt;хранит ссылку на объект контекста (Filter в данном случае). Этой переменной я воспользовался, когда добавлял или удалял фильтр.&lt;br /&gt;По умолчанию функции вызываются в контексте объекта в шаблоне (Filter), для этого в новой версии не нужно писать function () { ... }, есть более красиывый синтаксис например click: removeFilter($data). Но нам надо выполнять функции в контексте ViewModel поэтому мы пишем по старому, через function и выполняем функцию в контексте, котором нам надо.&lt;br /&gt;&lt;br /&gt;Вроде бы все.&lt;br /&gt;&lt;br /&gt;Теперь, как я говорил, добавим проверку. Если у нас хотя бы один фильтр без значения, то мы не будем добавлять новый.&lt;br /&gt;Обновленный класс ViewModel выглядит теперь так:&lt;br /&gt;&lt;iframe allowfullscreen="allowfullscreen" frameborder="0" src="http://jsfiddle.net/ivanov_vitaly/ZqX73/1/embedded/js/presentation/" style="height: 300px; width: 100%;"&gt;&lt;/iframe&gt; &lt;br /&gt;а фильтр так: &lt;br /&gt;&lt;br /&gt;&lt;iframe allowfullscreen="allowfullscreen" frameborder="0" src="http://jsfiddle.net/ivanov_vitaly/75fA3/6/embedded/js/presentation/" style="height: 300px; width: 100%;"&gt;&lt;/iframe&gt;&lt;br /&gt;и собственно результат:&lt;br /&gt;&lt;iframe allowfullscreen="allowfullscreen" frameborder="0" src="http://jsfiddle.net/ivanov_vitaly/75fA3/8/embedded/" style="height: 300px; width: 100%;"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;В следующей статье я напишу как писать расширения для knockout и мы сделаем фильтр по дате. Если у нас поле будет с типом Date, то к нему будет автоматически применяться jQuery date picker.&lt;br /&gt;&lt;br /&gt;Вопросы, коментарии преветсвуются!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-3057025627880993211?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/3057025627880993211/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2012/01/javascript-c-knockoutjs.html#comment-form' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/3057025627880993211'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/3057025627880993211'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2012/01/javascript-c-knockoutjs.html' title='Динамические фильтры на Javascript c помощью KnockoutJS'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-6354113651718903157</id><published>2011-12-22T18:49:00.001+02:00</published><updated>2011-12-22T19:11:03.140+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET MVC 3'/><category scheme='http://www.blogger.com/atom/ns#' term='HTML'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Performance'/><title type='text'>Отображение картинки из базы без запроса на сервер в MVС</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;Я искал альтернативный способ как можно отобразить картинку на странице не делая запроса на сервер, который отдает контент изображения. В основном, у всех вывод картинки на страницу выглядит так:&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://i54.tinypic.com/1h82m8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://i54.tinypic.com/1h82m8.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;если она у вас хранится на диске то первый варинат, если в базе то второй вариант. В обоих случая браузер посылает GET запрос на картинку. Если мы отображаем 10 записей, мы должны сделать 10 запросов чтоб получить картинки. А если мы делаем фотоальбом, отображаем штук 30 привьюх и храним картинки в базе, то грузиться будет дольше.&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;Решение которое я покажу называется &lt;a href="http://www.websiteoptimization.com/speed/tweak/inline-images/" target="_blank"&gt;inline image&lt;/a&gt;. Суть в том, что вы не делаете лишний запрос за картинкой, вы сразу встраиваете картинку в браузер. Выглядит это так&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: justify;"&gt;&lt;a href="http://i51.tinypic.com/2n0iohv.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://i51.tinypic.com/2n0iohv.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;В src просто пишется контет, закодированый через base64. Так выглядит мой хелпер, который может генерит такую картинку.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: true;"&gt; public static class ImageHelper&lt;br /&gt;    {&lt;br /&gt;        public static MvcHtmlString Image(this HtmlHelper html, byte[] imageBytes, string mime, object attributes)&lt;br /&gt;         {&lt;br /&gt;             TagBuilder img = new TagBuilder("img");&lt;br /&gt;&lt;br /&gt;             StringBuilder sb = new StringBuilder();&lt;br /&gt;             sb.AppendFormat("data:{0};", mime);&lt;br /&gt;             sb.AppendFormat("base64,{0}", Convert.ToBase64String(imageBytes));&lt;br /&gt;&lt;br /&gt;             img.MergeAttribute("src", sb.ToString());&lt;br /&gt;&lt;br /&gt;             return MvcHtmlString.Create(img.ToString(TagRenderMode.SelfClosing));&lt;br /&gt;         }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // пример использование&lt;br /&gt;    @Html.Image(Model.ToArray(), "image/jpg", new { width = 530, height = 390 })&amp;nbsp;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Конечно для картинок которые весяк около 30kb, src будет намного больше. Например чтоб отобразить такой значок в 35kb&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-18D4NgrQnl4/TvNhlL3B1fI/AAAAAAAABLc/aFWf6Nk_Ia8/s1600/subaru_logo_small.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-18D4NgrQnl4/TvNhlL3B1fI/AAAAAAAABLc/aFWf6Nk_Ia8/s1600/subaru_logo_small.jpg" /&gt;&amp;nbsp;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;у меня получилось&amp;nbsp;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://i51.tinypic.com/2c0dp5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://i51.tinypic.com/2c0dp5.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt; &lt;br /&gt;токо шире в 4 раза :)&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Итог&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Преимущества данного решения в том, что мы избавляемся от лишнего запроса на сервер и если размер картинки не велик, например, до 10 кб, то данное решение будет оптимально для отображения кучи картинок, если они у вас хранятся в байтах.&lt;br /&gt;Конечно у него есть и недостатки: что мне очень не нравится, то что картинки не кэшируются :/ (только если всунуть data в css) и эта возможность не поддерживается некоторыми версиями браузеров.&lt;br /&gt;&lt;br /&gt;В общем это было алтернативное решение, которое имеет место жить в частных случаях. Часто такое можно увидеть в css если посмотреть сайты yahoo, youtube, amazon и тп.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-6354113651718903157?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/6354113651718903157/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2011/12/mv.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/6354113651718903157'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/6354113651718903157'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2011/12/mv.html' title='Отображение картинки из базы без запроса на сервер в MVС'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://i54.tinypic.com/1h82m8_th.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-5220314858130433873</id><published>2011-12-20T19:27:00.001+02:00</published><updated>2011-12-21T12:50:15.069+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET MVC 3'/><category scheme='http://www.blogger.com/atom/ns#' term='Visual Studio'/><title type='text'>Настройка проекта для прекомпиляции cshtml файлов в ASP MVC 3</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Если вы писали на ASP MVC 3, то наверно сталкивались с проблемой, что ошибки во вью файлах не отображаются при компиляции проекта, их нет в Error List. Об ошибках вы можете узнать только если переклацаете весь проект :) Это происходит потому, что Razor вьюхи не компилируются когда вы билдите проект в студии.&lt;br /&gt;Можно сделать, чтоб вьюхи компилировались и ошибки во вьюхах отображались в Error List.&lt;br /&gt;&lt;br /&gt;Механика:&lt;br /&gt;&amp;nbsp;Открываем файл проекта в блокноте, находим в первой &lt;b&gt;Project Group&lt;/b&gt; секции тег &lt;b&gt;MvcBuildViews&lt;/b&gt; и меняем значение c &lt;b&gt;&lt;mvcbuildviews&gt;false&lt;/mvcbuildviews&gt;&lt;/b&gt; на &lt;b&gt;true &lt;/b&gt; и воаля!.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://i52.tinypic.com/70ystd.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;br /&gt;&lt;/a&gt;&lt;a href="http://i56.tinypic.com/amqwzp.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://i56.tinypic.com/amqwzp.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://i55.tinypic.com/34hudqv.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://i55.tinypic.com/34hudqv.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-5220314858130433873?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/5220314858130433873/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2011/12/cshtml-asp-mvc-3.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/5220314858130433873'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/5220314858130433873'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2011/12/cshtml-asp-mvc-3.html' title='Настройка проекта для прекомпиляции cshtml файлов в ASP MVC 3'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://i56.tinypic.com/amqwzp_th.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-305161681741009820</id><published>2011-12-11T16:10:00.001+02:00</published><updated>2011-12-12T01:18:35.205+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Knockout'/><category scheme='http://www.blogger.com/atom/ns#' term='UI'/><category scheme='http://www.blogger.com/atom/ns#' term='HTML'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>Что такое Knockout JS или MVVM на javascript. Пример</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Давно хотел написать что-то про &lt;a href="http://knockoutjs.com/" target="_blank"&gt;Knockout JS&lt;/a&gt; фреймворк. Если кто-то пишет сайты с интерфейсом где куча javascript, то вы найдете его для себя очень полезным, а потом не сможете без него ничего делать, т.к. к хорошему быстро привыкаешь.&lt;br /&gt;&lt;br /&gt;Что это такое и зачем надо ?&lt;br /&gt;&lt;br /&gt;Рассмотрим пример, сразу говорю выдумал на ходу, но он отображает суть.&lt;br /&gt;Вы можете купить продукт из списка, завернуть в подарочную упаковку,&amp;nbsp; указать надпись на коробке выбрать доставку и сразу видеть цену, которая меняется от выбранных опций и Summary, которое отображает выбранные опции как результат.&lt;br /&gt;При нажатии на 'Sumbit' отображает JSON, которы может поститься на сервер или тп.&lt;br /&gt;&lt;br /&gt;&lt;iframe allowfullscreen="allowfullscreen" frameborder="0" src="http://jsfiddle.net/ivanov_vitaly/gmbU4/1/embedded/result,js,html/presentation/" style="height: 300px; width: 100%;"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;Сколько бы вы делали этот пример просто на JS + jQuery ? Конечно, вы можете сказать "та нехер делать", но код будет выглядеть месивом из подписывания на события, if'ы для треканья зависимостей между элементами, что показать, какой скрыть, потом будете еще дэбажить, а потом начнете переосмысливать жизненные ценности и позавете Darth Vadera, чтоб он все разрулил :).&lt;br /&gt;&lt;br /&gt;На выходе &lt;br /&gt;&lt;br /&gt;- Набор элементов формы (View)&lt;br /&gt;- Данные, которые отражаются и отображаются в этих элементах (Model) и&amp;nbsp; бизнес логика, которая скрывает, отображает элементы и делает расчеты (Controller или ViewModel).&lt;br /&gt;&lt;br /&gt;На выходе &lt;br /&gt;- Нужно как-то это скомпоновать, чтоб зависимости и пересчеты резолвись сами, если какое-то значение изменится и никаких явных привязок к событиям через jQuery.&lt;br /&gt;&lt;br /&gt;Итого, как мы видим, получается MVC, но более точно это MVVM, шаблон группы MVx, который был придуман под технологию WPF и очень хорошо укладывается с возможностями этой технологии.&lt;br /&gt;В WPF это реализуется с помощью binding ("биндинга") и класса (ViewModel) с dependency property и routed events.&lt;br /&gt;&lt;br /&gt;Наш ViewModel и Dependancy Property&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://i55.tinypic.com/veaef7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://i55.tinypic.com/veaef7.png" /&gt;&amp;nbsp;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&amp;nbsp;Как мы видим у нас есть свойства, которые создаются с помощью observable. Так экоститема knockout знает, что если значение этого свойство набиндить на элемент, например, Address то это значение отобразится в текстовом поле. Если пользователь изменит значение текстового поля, новое значение запишется обратно в свойство (Address), с которым связан элемент через observable. Получается двухсторонняя связь. Можно сделать и в одну сторону, это потом.&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Наш Binding&amp;nbsp; &lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img border="0" height="452" src="http://i54.tinypic.com/3354b29.png" width="640" /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;У нас есть один атрибут &lt;b&gt;datа-bind&lt;/b&gt;, в которм мы указываем с каким свойством (значением) связывать этот элемент и дополнительные опции. Для разных элементов свойства привязываются по разному, но в общем значение datа-bind начинается так - &lt;u&gt;"value: Property"&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;Рассмотрим по порядку:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;select data-bind="options: Products, value: Product"&lt;/b&gt; - это значит, что мы биндим список продуктов на выпадающий список и если выбранный продукт изменится, это значение запишется в свойство Product. Если бы мы указали viewModel.Product = ko.observable("Jeans") то сразу выбраными были бы "Jeans".&lt;br /&gt;&lt;b&gt;input type="radio" data-bind="checked: GiftWrap"&lt;/b&gt; - это значит, что мы биндим value радиокнопки на свойство GiftWrap. Какая радиокнопка выбрана - такое значение и будет "Yes" или "No". Ничего не выбранно - значит ничего не будет, null.&lt;br /&gt;&lt;b&gt;div data-bind="visible: GiftWrap() == 'Yes'"&lt;/b&gt; - тут начинается интересное. С помощью visible мы решаем будет ли элемент отображатся по условию после двоеточия. Если мы поставим радиокнопку в Yes, будет div style="display: none", в другом случае div style="display: block". Как это работает ? Как только одно из observable свойств меняется, экосистема knockout проверяет весь биндинг и пересчитыват условия в атрибутах.&lt;br /&gt;С простыми свойствами я думаю несложно. Давайте рассмотрим этот кусок&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://i52.tinypic.com/mimjdf.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://i52.tinypic.com/mimjdf.png" /&gt;&lt;/a&gt;&lt;/div&gt;и код&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://i51.tinypic.com/2s9xboj.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://i51.tinypic.com/2s9xboj.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Если бы у нас было observable, мы бы не смогли посчитать Total, т. к. оно зависит от других значений свойств. Для этого мы используем новый тип свойства &lt;b&gt;dependentObservable&lt;/b&gt;, который обозначает, что это свойство считается динамически. Сколько свойств во ViewModel изменится, столько раз knockout пересчитает свойство Total. Нам остается только отобразить это свойство в &lt;b&gt;span data-bind="text: Total"&lt;/b&gt;. Это все работает автоматически !!! Достаточно только поставить нужное значение и опции в атрибут data-bind. Точно также работает Summary.&lt;br /&gt;Я не буду дальше описывать в деталях синтаксис и тп, на сайте knockout очень хорошо все расписано с отличными примерами. Моя цель была заинтересовать и показать, как можно избавиться от кучи ненужной привязки к событиям через jQuery, месива, рассредоточеной бизнес логики и других "прелестей" при работе с сложным UI и JS.&lt;br /&gt;Я считают, что этот фреймворк должен быть на счету у каждого серъезного разработчика.&lt;br /&gt;&lt;br /&gt;Что может этот фреймворк:&lt;br /&gt;- простой биндинг, поле-свойство&lt;br /&gt;- сложный биндинг, динамическое значение поля&lt;br /&gt;- можете писать свои настройки типо как value, checked и тп. &lt;br /&gt;- можете использовать jTemplate&lt;br /&gt;- можете использовать ajax&lt;br /&gt;- можете биндить события, типо click, chenched и тп&lt;br /&gt;- можете биндить не только на значение элемента, но и например явно на атрибуты или стили, например если вы хотите чтоб в style свойство color менялось если сумма больше $20 и тп.&lt;br /&gt;- можете делать все что угодно, если мой пример отображает суть вашей проблемы.&lt;br /&gt;&lt;br /&gt;Мне очень нравится этот фреймворк. Читаешь доку, смотришь код и все как-то интуитивно понятно, запоминай только опции в атрибутах, а так работать одно удовольствие. Уже не раз сокращал старый код в раза полтора-два точно. Но это не главное, а главное что бизнес логика в одном месте, а вьюха в другом.&lt;br /&gt;Есть еще подобный фреймворк &lt;a href="http://documentcloud.github.com/backbone/" target="_blank"&gt;backbones&lt;/a&gt;, но мне он показался ужасным и более низкоуровневым, там надо много писать руками, очен много, но у него есть свои преимущества.&lt;br /&gt;&lt;br /&gt;Если вам интересна данная тема и фреймворк - напиши в коментах ваши пожелания или мысли, стоит ли мне развивать эту тему и писать другие примеры, реальные, которые я применял в проектах.&lt;br /&gt;Отвечу на любый вопросы. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-305161681741009820?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/305161681741009820/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2011/12/knockout-js.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/305161681741009820'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/305161681741009820'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2011/12/knockout-js.html' title='Что такое Knockout JS или MVVM на javascript. Пример'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://i55.tinypic.com/veaef7_th.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-6838183885875039736</id><published>2010-11-18T15:21:00.002+02:00</published><updated>2010-11-18T15:22:17.743+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='AJAX'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='ScriptManager'/><category scheme='http://www.blogger.com/atom/ns#' term='Script Combining'/><title type='text'>XML Parsing Error: no element found</title><content type='html'>Гугл говорит, что не только я сталкивался с такой проблемой, но причина к сожалению у каждого разная. Но в результате все видят типо:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://i53.tinypic.com/23th44g.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://i53.tinypic.com/23th44g.png" /&gt;&lt;/a&gt;&lt;/div&gt;и пустой html.&lt;br /&gt;&lt;br /&gt;Свою ошибку вы можете посмотреть в Application_Error. У меня была "The resource URL cannot be longer than 1024 characters. If using a CompositeScriptReference, reduce the number of ScriptReferences it contains, or combine them into a single static file and set the Path property to the location of it." Потому, что в в ScripMapanger\CompositeScript было много скриптов. До этого я прогнал все файлы в JSLint, но ошибок не нашел. Вот такое г..вно этот CompositeScript, лучше самому скомбинировать и перезжать на продакшен. Как-нибудь напишу решение.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-6838183885875039736?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/6838183885875039736/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2010/11/xml-parsing-error-no-element-found.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/6838183885875039736'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/6838183885875039736'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2010/11/xml-parsing-error-no-element-found.html' title='XML Parsing Error: no element found'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://i53.tinypic.com/23th44g_th.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-5180961933592340847</id><published>2010-10-24T14:46:00.000+03:00</published><updated>2010-10-24T14:46:58.947+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Caching'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Performance'/><title type='text'>Как заставить браузер принудительно кэшировать картинки</title><content type='html'>Недавно сталкнулся с проблемой, когда динамически меняешь картинки жаваскриптом, то они мигают при смене первое время. Это из-за того что тратится время на загрузку по требованию. Для решения проблемы надо просто картинки подгрузить раньше на загрузке страницы, выглядит это так:&lt;br /&gt;&lt;pre class="brush: javascript; gutter: false;"&gt;var preloadImages = {};&lt;br /&gt;preloadImages["image1"] = 'Images/image1.png';&lt;br /&gt;preloadImages["image2"] = 'Images/image2.png';&lt;br /&gt;...&lt;br /&gt;preloadImages["image3"] = 'Images/image3.png';&lt;br /&gt;preloadImages["default"] = 'Images/default.png';&lt;br /&gt;&lt;br /&gt;function changeImage(key) {&lt;br /&gt;    if (typeof key != 'undefined') {&lt;br /&gt;        $('#image').attr('src', preloadImages[key]);&lt;br /&gt;    }&lt;br /&gt;    else {&lt;br /&gt;        $('#image').attr('src', preloadImages["default"]);&lt;br /&gt;    }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;(function loadImages() {&lt;br /&gt;    for (key in preloadImages) {&lt;br /&gt;    var img = new Image();&lt;br /&gt;    img.src = preloadImages[key];&lt;br /&gt;    }&lt;br /&gt;})();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Затем можно использовать типо onmouseover="changeImage('image1');" onmouseout="changeImage();".&lt;br /&gt;По идее все должно работать, но картинки все равно мигают :/ Firebug показывает, что картинки подгружены. Но тогда я увидел следующее&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_8B_3XCoKE6Q/TMQWth7nE-I/AAAAAAAAAic/DSyvZsRAnxQ/s1600/SS-2010-10-24_14.19.22.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_8B_3XCoKE6Q/TMQWth7nE-I/AAAAAAAAAic/DSyvZsRAnxQ/s1600/SS-2010-10-24_14.19.22.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;В запросе идет хедер, что кэшировать не надо, а в ответе нифига. Картинка просто не кэшируется и каждый раз когда я меняю src она запрашивается заново. Осталось включить кэширование картинок и вроде бы должно заработать. Настройку кэширования статического контента в IIS6 можно посмотреть &lt;a href="http://www.codeproject.com/KB/aspnet/CachingImagesInASPNET.aspx"&gt;здесь&lt;/a&gt;. Ниже показано как настроить тоже самое в IIS7.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_8B_3XCoKE6Q/TMQYO3yGNLI/AAAAAAAAAig/dgFbHzmm0x4/s1600/SS-2010-10-24_14.26.45.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_8B_3XCoKE6Q/TMQYO3yGNLI/AAAAAAAAAig/dgFbHzmm0x4/s1600/SS-2010-10-24_14.26.45.png" /&gt;&lt;/a&gt;&lt;/div&gt;Сначала выбираем папку с картинками (тоже самое можно сделать и для css папка), затем открываем "Заголовки ответов HTTP".&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_8B_3XCoKE6Q/TMQY2LZGGCI/AAAAAAAAAik/ze_c9nL5arU/s1600/SS-2010-10-24_14.29.34.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_8B_3XCoKE6Q/TMQY2LZGGCI/AAAAAAAAAik/ze_c9nL5arU/s1600/SS-2010-10-24_14.29.34.png" /&gt;&lt;/a&gt;&lt;/div&gt;Последовательность действий сверху вниз. После нажатия OK у вас будет 3 новых вещи:&lt;br /&gt;1. В папке для которой мы настраивали кеш (Img) появится web.config (его надо включить в solution).&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_8B_3XCoKE6Q/TMQZWVVNzRI/AAAAAAAAAio/fenKXIYYhcE/s1600/SS-2010-10-24_14.32.22.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_8B_3XCoKE6Q/TMQZWVVNzRI/AAAAAAAAAio/fenKXIYYhcE/s1600/SS-2010-10-24_14.32.22.png" /&gt;&amp;nbsp;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;2. В заголовке ответа появится хедер Cache-Control, говорящий о том, что картинка кешируется. Даже если следующий раз браузер захочет потянуть картинку, то в firebug вы увидите в ответ на GET запрос не 200 код, а 206. Значит картинка берется из кэша.&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_8B_3XCoKE6Q/TMQaz7qB4-I/AAAAAAAAAiw/kup2Gif3b34/s1600/SS-2010-10-24_14.37.45.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_8B_3XCoKE6Q/TMQaz7qB4-I/AAAAAAAAAiw/kup2Gif3b34/s1600/SS-2010-10-24_14.37.45.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;3. Ну и 3 это результат, картинки не мигают.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-5180961933592340847?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/5180961933592340847/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2010/10/blog-post.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/5180961933592340847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/5180961933592340847'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2010/10/blog-post.html' title='Как заставить браузер принудительно кэшировать картинки'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_8B_3XCoKE6Q/TMQWth7nE-I/AAAAAAAAAic/DSyvZsRAnxQ/s72-c/SS-2010-10-24_14.19.22.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-7224315912368676843</id><published>2010-05-18T14:27:00.000+03:00</published><updated>2010-05-18T14:27:09.555+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><title type='text'>Как отформатировать сжатый JS файл</title><content type='html'>Иногда для отладки или для личных нужд вам нужно выровнять сжатый JS файл, который представлен в виде строки, без отступов, пробелов, переносов и тп. Это можно сделать &lt;a href="http://jsbeautifier.org/"&gt;online&lt;/a&gt; - вставил, нажал, скопировал обратно (можно выставить размер отступов и тп). И там же есть ссылка на саму утилиту, она open source.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-7224315912368676843?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/7224315912368676843/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2010/05/js.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/7224315912368676843'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/7224315912368676843'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2010/05/js.html' title='Как отформатировать сжатый JS файл'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-1906774563573291715</id><published>2010-05-11T16:03:00.000+03:00</published><updated>2010-05-11T16:03:52.165+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Debug'/><category scheme='http://www.blogger.com/atom/ns#' term='IIS'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>Как определить к какому пулу относиться w3wp процесс в IIS 7</title><content type='html'>Если у вас сайт развернут на IIS 7, для того чтоб его отдебажить, надо приэтачиться к процессу w3wp.exe. В одно время у вас может работать несколько сайтов и каждый может принадлежать к своему пулу. Когда вы этачитесь к процессу (рисунок ниже), то начинаем гадать, попал не попал, подсветились брейкпоинты или нет :))&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_8B_3XCoKE6Q/S-lSpU-EbAI/AAAAAAAAAhs/uXuZuuT-54E/s1600/SS-2010-05-11_15.50.31.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="59" src="http://1.bp.blogspot.com/_8B_3XCoKE6Q/S-lSpU-EbAI/AAAAAAAAAhs/uXuZuuT-54E/s640/SS-2010-05-11_15.50.31.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Для того чтоб точно попасть, можно посмотреть, что к чему относится с помощью утилиты для администрирования IIS с командной строки - APPCMD, которая находится по пути '%systemroot%\system32\inetsrv\'. Детально можете разобраться по докам или '/?'. Нам нужно только  'WP администрирование рабочих процессов'. Пример использования и результат ниже. Мы видим ID процесса и пул к которому сайт в этом процессе относится.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_8B_3XCoKE6Q/S-lUAHcdq8I/AAAAAAAAAh0/3Rn_S0EoHuY/s1600/SS-2010-05-11_15.55.36.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="146" src="http://1.bp.blogspot.com/_8B_3XCoKE6Q/S-lUAHcdq8I/AAAAAAAAAh0/3Rn_S0EoHuY/s640/SS-2010-05-11_15.55.36.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-1906774563573291715?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/1906774563573291715/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2010/05/w3wp-iis-7.html#comment-form' title='Комментарии: 4'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/1906774563573291715'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/1906774563573291715'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2010/05/w3wp-iis-7.html' title='Как определить к какому пулу относиться w3wp процесс в IIS 7'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_8B_3XCoKE6Q/S-lSpU-EbAI/AAAAAAAAAhs/uXuZuuT-54E/s72-c/SS-2010-05-11_15.50.31.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-913278598714127223</id><published>2010-05-07T01:55:00.005+03:00</published><updated>2010-05-07T02:08:32.108+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Bulk Operations'/><category scheme='http://www.blogger.com/atom/ns#' term='Usability'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>Индикация прогресса при длительных операциях в ASP.NET</title><content type='html'>Я думаю вы сталкивались с ситуацией, когда нужно что-то импортить, например CSV файл с продуктами, кастомерами, синхронизировать базы или работать с каким то тормознутым веб сервисом. Во всех перечисленных и подобных случаях страница "лочится" пока не закончится операция, но нам хотелось бы видеть какой-то прогресс, о чем сегодня и поговорим.&lt;br /&gt;Недавно мне нужно было импортить продукты из индусского сервиса, не важно как, главное что страница лочилась на 1-2 минуты и непонятно, что там происходило и когда оно завершалось.&lt;br /&gt;Существует несколько решений как можно узнать прогресс на длительной операции:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Сохранять прогресс в сессию, а на клиенте должен быть таймер, который после запуска длительной операции будет опрашивать страницу с какой-то периодичностью, например каждую секунду или тп. Этот метод описан &lt;a href="http://devarchive.net/displaying_progress_bar_for_long_running_processes.aspx"&gt;здесь&lt;/a&gt;;&lt;/li&gt;&lt;li&gt;Создать вспомогательную страницу в iframe, при запуске которой в OnLoad сразу начинается длительная операция. Во время прогресса надо писать в Response вызов JS функции, которая будет вызываться в родительском окне, с которого был запущен iframe. Этот метод описан &lt;a href="http://encosia.com/2007/10/03/easy-incremental-status-updates-for-long-requests/"&gt;здесь&lt;/a&gt;;&lt;/li&gt;&lt;li&gt;Сделать страницу асинхронной, через Async или &lt;a href="http://msdn.microsoft.com/en-us/library/system.web.ui.icallbackeventhandler.aspx"&gt;ICallbackEventHandler Interface&lt;/a&gt; и опять-таки через таймер опрашивать сервер. Это способ можно отнести как вариация второго.&lt;/li&gt;&lt;/ol&gt;Из всех перечисленных мне не нравится никакой :)&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Потому, что надо делать вспомогательную страницу. Хоть автор и советует использовать только этот метод, но мне он кажется недоделаным.&lt;/li&gt;&lt;li&gt; Потому, что както-то по индусски... опрашивать постоянно сервер? может в каком-то случае это и будет лучший вариант, но не сегодня.&lt;/li&gt;&lt;li&gt;Ограниченные возможности по передаче параметров от клиента к серверу и наоборот. Можно передать, что угодно, но только в одной строке. А дальше думать формат, парсить... ну и + оргументы выше в пункте 2.&lt;/li&gt;&lt;/ol&gt;Мое решение похожее больше на 1 вариант, но без iframe. В результате  получим примерно следующее:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_8B_3XCoKE6Q/S-M-LewEuaI/AAAAAAAAAhM/LHYSaF5MDqw/s1600/SS-2010-05-07_01.09.18.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="105" src="http://3.bp.blogspot.com/_8B_3XCoKE6Q/S-M-LewEuaI/AAAAAAAAAhM/LHYSaF5MDqw/s400/SS-2010-05-07_01.09.18.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_8B_3XCoKE6Q/S-M-yjKlM0I/AAAAAAAAAhU/Z4O5WJ_O2IU/s1600/SS-2010-05-07_01.11.39.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="111" src="http://1.bp.blogspot.com/_8B_3XCoKE6Q/S-M-yjKlM0I/AAAAAAAAAhU/Z4O5WJ_O2IU/s400/SS-2010-05-07_01.11.39.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;На картинках выше показана индикация прогресса во время импорта продуктов, категорий и брендов из сервиса. Initialization показывает прогресс когда выполняется аутентификация или может создание вспомогательный  классов, по вашему желанию. В мое случае я логинился на сервис. А теперь как это делалось. Для этого нам понадобится &lt;b&gt;одна&lt;/b&gt;! страница, класс с длительной операцией, интерфейс через который будет уведомляться страница о прогрессе, пару js функций которые будут двигать прогресс бар и немного css для прогресс бара.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: true;"&gt;public interface IProgressReporter&lt;br /&gt;    {&lt;br /&gt;        void ReportInitializationFinished();&lt;br /&gt;        void ReportCategoriesProgress(int count, int completed);&lt;br /&gt;        void ReportProductsProgress(int count, int completed);&lt;br /&gt;        void ReportBrandsProgress(int count, int completed);&lt;br /&gt;        void ReportException(Exception exception);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Методы интерфейса как я и говорил мы будет дергать из класса, который занимает длительным процессом. Ниже приведен сам класс, он примитивный, просто чтоб показать саму суть.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: true;"&gt;public class LongOpperations&lt;br /&gt;    {&lt;br /&gt;        public void ProcessCatalog(IProgressReporter progressReporter)&lt;br /&gt;        {&lt;br /&gt;            // some initialization, web service authentication, whatever&lt;br /&gt;            Thread.Sleep(5000);&lt;br /&gt;            progressReporter.ReportInitializationFinished();&lt;br /&gt;&lt;br /&gt;            for (int i = 0; i &amp;lt; 100; i++)&lt;br /&gt;            {&lt;br /&gt;                Thread.Sleep(100);&lt;br /&gt;                progressReporter.ReportCategoriesProgress(100, i + 1);&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            for (int i = 0; i &amp;lt; 20; i++)&lt;br /&gt;            {&lt;br /&gt;                Thread.Sleep(200);&lt;br /&gt;                progressReporter.ReportBrandsProgress(20, i + 1);&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            for (int i = 0; i &amp;lt; 100; i++)&lt;br /&gt;            {&lt;br /&gt;                Thread.Sleep(200);&lt;br /&gt;                progressReporter.ReportProductsProgress(100, i + 1);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;Отчетом о прогрессе выступает наша страница, для этого нужно реализовать наш интерфейс. Потом, когда вы будем стартовать длительную процедуру, мы передадим себя, страницу, в наш обработчик. В итоге получаем подобие шаблона Visitor. Самое интересное находится в методе Render. Начало ясное, если мы не нажали кнопку, то просто рендерим страницу как есть. Дальше мы должны отключить буферизацию страницы, в другом случае IE например закеширует первые 256 байт ответа и мы получим не то, что ожидали. Поэтому отключаем. Если мы после этого начнем репортить прогресс, то мы перетрем то, что существует на странице, поэтому ренедрим её как есть, а потом будет дописывать в Response наши ответы. Основная идея заключается в том, чтоб в Response записать вызов клиентской функции и сразу вывести его на страницу через Flush. Как только на странице появится script, он сразу выполнится, тем самым подвинет прогресс бары.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: true;"&gt;public partial class _Default : Page, IProgressReporter&lt;br /&gt;    {&lt;br /&gt;        private bool started;&lt;br /&gt;        protected override void Render(HtmlTextWriter writer)&lt;br /&gt;        {&lt;br /&gt;            if (!started)&lt;br /&gt;            {&lt;br /&gt;                base.Render(writer);&lt;br /&gt;                return;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            Response.BufferOutput = false;&lt;br /&gt;            base.Render(writer);&lt;br /&gt;            Response.Flush();&lt;br /&gt;&lt;br /&gt;            ReportInitializationStarted();&lt;br /&gt;            new LongOpperations().ProcessCatalog(this);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        protected void OnStartClick(object sender, EventArgs e)&lt;br /&gt;        {&lt;br /&gt;            started = true;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public void ReportInitializationStarted()&lt;br /&gt;        {&lt;br /&gt;            Response.Write("&amp;lt;script&amp;gt;ReportInitializationStarted();&amp;lt;/script&amp;gt;");&lt;br /&gt;            Response.Flush();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public void ReportInitializationFinished()&lt;br /&gt;        {&lt;br /&gt;            Response.Write("&amp;lt;script&amp;gt;ReportInitializationFinished();&amp;lt;/script&amp;gt;");&lt;br /&gt;            Response.Flush();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public void ReportCategoriesProgress(int count, int completed)&lt;br /&gt;        {&lt;br /&gt;            ReportProgress(pnlCategoryProgress.ClientID, lblCategory.ClientID, count, completed);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public void ReportProductsProgress(int count, int completed)&lt;br /&gt;        {&lt;br /&gt;            ReportProgress(pnlProductProgress.ClientID, lblProduct.ClientID, count, completed);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public void ReportBrandsProgress(int count, int completed)&lt;br /&gt;        {&lt;br /&gt;            ReportProgress(pnlBrandProgress.ClientID, lblBrand.ClientID, count, completed);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public void ReportException(Exception exception)&lt;br /&gt;        {&lt;br /&gt;            string progressCall = string.Format("&amp;lt;script&amp;gt;ReportException('{0}','{1}');&amp;lt;/script&amp;gt;", exception.Message, exception.StackTrace);&lt;br /&gt;&lt;br /&gt;            Response.Write(progressCall);&lt;br /&gt;            Response.Flush();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private void ReportProgress(string progressBar, string percentLabel, int count, int completed)&lt;br /&gt;        {&lt;br /&gt;            double progress = (count == 0) ? 100 : (100.0 / count) * completed;&lt;br /&gt;&lt;br /&gt;            string progressCall = string.Format("&amp;lt;script&amp;gt;UpdateProgress('{0}','{1}',{2},{3},{4});&amp;lt;/script&amp;gt;", progressBar, percentLabel, progress, count, completed);&lt;br /&gt;&lt;br /&gt;            Response.Write(progressCall);&lt;br /&gt;            Response.Flush();&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Клиентский функции, которые двигают прогресс бары и тп.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: javascript; gutter: true;"&gt;function UpdateProgress(progressBarId, percentLabelId, progress, count, completed) {&lt;br /&gt;            var progressBar = document.getElementById(progressBarId);&lt;br /&gt;            var progressLabel = document.getElementById(percentLabelId);&lt;br /&gt;            &lt;br /&gt;            progressLabel.innerHTML = (progress == -1) ? '0' : completed + '/' + count;&lt;br /&gt;            progressBar.style.width = progress + '%';            &lt;br /&gt;        };&lt;br /&gt;&lt;br /&gt;        function ReportException(message, stack) {&lt;br /&gt;            var p = document.createElement('p');&lt;br /&gt;            p.innerHTML = 'Message: ' + message + '&lt;br /&gt; Stack: ' + stack;&lt;br /&gt;            document.getElementById('error').appendChild(p);&lt;br /&gt;        };&lt;br /&gt;&lt;br /&gt;        function ReportInitializationFinished() {&lt;br /&gt;            var pnl = document.getElementById('pnlInitializing');&lt;br /&gt;            pnl.className = null;&lt;br /&gt;        };&lt;br /&gt;&lt;br /&gt;        function ReportInitializationStarted() {&lt;br /&gt;            var pnl = document.getElementById('pnlInitializing');&lt;br /&gt;            pnl.className = 'progress';&lt;br /&gt;            pnl.style.width = '100%'            &lt;br /&gt;        };&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Вот еще кусок разметки для ясности. Весь приводить не буду, т.к. там тоже самое и немного css.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_8B_3XCoKE6Q/S-NHRrHVMqI/AAAAAAAAAhc/MF8jafZmWlE/s1600/SS-2010-05-07_01.47.56.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="252" src="http://1.bp.blogspot.com/_8B_3XCoKE6Q/S-NHRrHVMqI/AAAAAAAAAhc/MF8jafZmWlE/s640/SS-2010-05-07_01.47.56.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;Вот и все. Основная идея в том, чтоб запустив длительный процесс сразу отдавать ответ в виде js сриптовой инъекции, которая будет обновлять прогресс. Вы можете наделать любых методов в интерфейсе. Они могут быть настолько детальными на сколько вам необходимо. В моем случае я хотел еще справа от прогресс баров добавить лог ошибок и добавил соответсвующий метод в инерфейсе ReportException, оставил для примера.&lt;br /&gt;Буду рад услышать ваши идеи и отзывы.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-913278598714127223?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/913278598714127223/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2010/05/aspnet.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/913278598714127223'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/913278598714127223'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2010/05/aspnet.html' title='Индикация прогресса при длительных операциях в ASP.NET'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_8B_3XCoKE6Q/S-M-LewEuaI/AAAAAAAAAhM/LHYSaF5MDqw/s72-c/SS-2010-05-07_01.09.18.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-1317893882623800116</id><published>2010-03-22T17:58:00.001+02:00</published><updated>2010-03-22T17:59:11.621+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>Способ перевести сайт в maintenance mode</title><content type='html'>Вопрос очень простой, но в основном те решения, которые я встречал сводились к созданию второго сайта. Один был рабочий, а когда его надо было обновить включали второй сайт, а первый тушили. Еще был вариант с модулем, который по ключу в конфиге включался или выключался и показывал соответствующую страницу maintenance mode.&lt;br /&gt;&lt;br /&gt;В asp.net 2.0 появился способ сделать это удобно и красивей. Вам нужно просто создать файл с названием app_offline.htm (именно так, по другому работать не будет) и положить его в корень своего сайта, после этого asp.net будет обрубать все &lt;u&gt;новые запросы&lt;/u&gt;, а старые нормально отработаю до очередного нового.&lt;br /&gt;&lt;br /&gt;Когда вы закончите обновлять сайт, просто переименуйте файл или удалите его и все заработает.&lt;br /&gt;По умолчанию, если размер app_offline файла меньше 512 кб, то IE будет показывать friendly error page. &lt;br /&gt;&lt;br /&gt;Очень удобно, удачи.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-1317893882623800116?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/1317893882623800116/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2010/03/maintenance-mode.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/1317893882623800116'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/1317893882623800116'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2010/03/maintenance-mode.html' title='Способ перевести сайт в maintenance mode'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-4371623997508628557</id><published>2010-03-15T16:21:00.000+02:00</published><updated>2010-03-15T16:21:56.581+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='хобби'/><title type='text'>Доделал Tomcat F14-A</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_8B_3XCoKE6Q/S55ASPjIqLI/AAAAAAAAAhA/UF-gDiMLp0E/s1600-h/Tomcat+F14-A.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="356" src="http://1.bp.blogspot.com/_8B_3XCoKE6Q/S55ASPjIqLI/AAAAAAAAAhA/UF-gDiMLp0E/s640/Tomcat+F14-A.jpg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;На днях закончил очередную модель Tomcat F14-A (modelist). Сама модель достаточно хорошо исполнена, глубокие прорези, подгонять детали напильником особо не пришлось. Также хочется отметить качество декалей, не рвутся, накладываются легко. Единственная проблема, это то, что декали которые шли в комплекте не понятно к какой эскадрильи относятся, обычно на килях черепа или пегасы (каталог dragon 2008). &lt;br /&gt;В общем я доволен, даже меняется стреловидность крыла, прикольно :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-4371623997508628557?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/4371623997508628557/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2010/03/tomcat-f14.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/4371623997508628557'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/4371623997508628557'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2010/03/tomcat-f14.html' title='Доделал Tomcat F14-A'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_8B_3XCoKE6Q/S55ASPjIqLI/AAAAAAAAAhA/UF-gDiMLp0E/s72-c/Tomcat+F14-A.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-5473027788557409326</id><published>2010-03-03T11:43:00.006+02:00</published><updated>2010-03-03T21:06:01.719+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='AJAX'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>Как подружить ASP.NET AJAX Control контекст c функциями jQuery?</title><content type='html'>Когда вы пишите AJAX Enabled контролы, то в JS классе для обращения к внутренним свойствам, функциям и тп. вы используете '&lt;b&gt;this&lt;/b&gt;'. При использовании jQuery, при вызове метода ставиться контекст элемента, для которого вы вызываете функцию. Например в each если обратится к this, то получите текущий элемент итератора. Также используя jQuery UI, события которые срабатывают в плагинах, вызываются в контексте элемента. В общем обработчик выглядит так (для примера с draggable плагином):&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: javascript; gutter: false;"&gt;$('.accept-siblings,.accept-children').droppable({&lt;br /&gt;        accept: 'li.leaf-draggable'&lt;br /&gt;        , greedy: true&lt;br /&gt;        , tolerance: 'pointer'&lt;br /&gt;        , drop: this._onDropHandler&lt;br /&gt;        , over: this._onDragOverHandler&lt;br /&gt;        , out: this._onDragOutHandler&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;_onDropHandler: function(event, ui) {&lt;br /&gt;    // this - будет элемент, над которым мы отпускаем мышь.&lt;br /&gt;    // к внутренним членам вашего ajax класса обратиться не получится.&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Для решения нам главное пропихнуть контекст AJAX класса. Использую Function.createDelegate мы не выкрутимся, т.к. он &lt;b&gt;заменят&lt;/b&gt; контекст вызова, в нашем случае в обработчике &lt;b&gt;this&lt;/b&gt; будет не элемент, над которым мы отпускаем мышь, а AJAX класс и мы никак не узнаем куда дропать. &lt;br /&gt;Для того чтоб пропихнуть контекст не заменяя его, нам нужен &lt;a href="http://msdn.microsoft.com/en-us/library/bb882547.aspx"&gt;Function.createCallback&lt;/a&gt;. Сигнатура такая же, только смысл другой, мы не заменяем контекст, а &lt;b&gt;передаем параметр&lt;/b&gt; не изменяя контекст. Теперь в обработчике будет добавляться параметр, который мы передаем:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: jscript; gutter: false;"&gt;this._onDropCallback = Function.createCallback(this._onDropHandler, this);&lt;br /&gt;&lt;br /&gt;$('.accept-siblings,.accept-children').droppable({&lt;br /&gt;    ...&lt;br /&gt;    , drop: this._onDropCallback&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;_onDropHandler: function(event, ui, context) {&lt;br /&gt;    // this - так и остается элементом над которым мы отпускаем мышь&lt;br /&gt;    // к членам AJAX класса можно обращаться через context. Например context.get_element();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-5473027788557409326?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/5473027788557409326/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2010/03/aspnet-ajax-control-c-jquery.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/5473027788557409326'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/5473027788557409326'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2010/03/aspnet-ajax-control-c-jquery.html' title='Как подружить ASP.NET AJAX Control контекст c функциями jQuery?'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-8175799370646960816</id><published>2010-02-25T12:10:00.004+02:00</published><updated>2010-02-25T12:15:21.616+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='LINQ'/><title type='text'>Улучшение производительности LINQ запросов. Precompiled LINQ Queries.</title><content type='html'>Одна из возможностей LINQ to SQL и LINQ to Entities это транслятор запросов, которой из программного кода переводит запрос на язык, понимаемый источником, к которому вы посылаете запрос (если речь идет о БД). Процесс преобразования вашей цепочки вызовов, например, в T-SQL не дешевая операция. Каждый раз когда вы выполняете запрос, строится дерево выражений, операций, по которым ваш провайдер, в данном случае SQLProvider, будет транслировать запрос в T-SQL. &lt;br /&gt;&lt;br /&gt;Если у вас есть веб сервисы, который дергаются на клиенте, то хочется чтоб они выполнялись быстро, чтоб продолжить выполнение каких-то операций на клиенте. А если у вас AJAX и Rich UI, то тем более хочется чтоб отклик был как можно быстрей. Конечно, это относится не только к сервисам, а и к приложению в целом. &lt;br /&gt;&lt;br /&gt;Если у вас есть запросы, которые выполняются часто, например GetCustomerByName, GetPostsByAuthor и тп, и вы знаете что в запросе не будут добавляться фильтры то их можно оптимизировать. Под оптимизацией я понимаю кэширование процесса построения запроса, а именно скомпилировать запрос. Другими словами вы получаете хранимую процедуру в виде LINQ запроса в которую вы просто передаете параметры. &lt;br /&gt;&lt;br /&gt;Рассмотри пример. У вас есть форма, в который вы ищете страну, чтоб показать её на карте или тп., запрос будет выглядеть примерно так:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;using (DBContext db = new DBContext())&lt;br /&gt;{&lt;br /&gt;    var countries = (from country in db.Countries&lt;br /&gt;                     where country.Name.ToLower() == key.ToLower()&lt;br /&gt;                     select country).ToList()&lt;br /&gt;&lt;br /&gt;    // do actions with result.&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Компилируется запрос так:&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;CompiledQuery.Compile&amp;lt;DBContext, string, IQueryable&amp;lt;Country&amp;gt;&amp;gt;(&lt;br /&gt;    (db, key) =&amp;gt; (from country in db.Countries&lt;br /&gt;                  where country.Name.ToLower() == key.ToLower()&lt;br /&gt;                  select country));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Compile принмиает Func&amp;lt;&amp;gt;, можете передать контекст, дополнительные параметры и возвращаемый результат. Потом по коду можно использовать его так:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;IQueryable&amp;lt;Country&amp;gt; countries = GetCountryByName.Invoke(db, txtCountry.Text);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Если у вас web приложение можно сохранить запрос в статическую переменную. В другом случае - "шило на мыло".&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;public Func&amp;lt;DBContext, string, IQueryable&amp;lt;Country&amp;gt;&amp;gt; GetCountryByName =&lt;br /&gt;            CompiledQuery.Compile&amp;lt;DBContext, string, IQueryable&amp;lt;Country&amp;gt;&amp;gt;(&lt;br /&gt;    (db, key) =&amp;gt; (from country in db.Countries&lt;br /&gt;                  where country.Name.ToLower() == key.ToLower()&lt;br /&gt;                  select country));&lt;br /&gt;&lt;/pre&gt;Если у вас есть Repository классы, можно компилировать запрос при первом обращении. Для того чтоб код не "вонял", можно вынести запрос  в отдельный класс Queries. При желании можно создать и подклассы, например Queries.Location.GetCountryByName:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;public static IQueryable&amp;lt;Country&amp;gt; GetCountryByName(int name)&lt;br /&gt;{&lt;br /&gt;    if (Queries.Location.GetCountryByName == null)&lt;br /&gt;    {&lt;br /&gt;        Queries.Location.GetCountryByName = &lt;br /&gt;            CompiledQuery.Compile&amp;lt;DBContext, string, IQueryable&amp;lt;Country&amp;gt;&amp;gt;(&lt;br /&gt;    (db, key) =&amp;gt; (from country in db.Countries&lt;br /&gt;                  where country.Name.ToLower() == key.ToLower()&lt;br /&gt;                  select country));&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return Queries.Location.GetCountryByName.Invoke(_context, name);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Если у вас в результате запроса возвращается сложный тип (анонимный), нужно создать его проекцию, например CountryDTO.&lt;br /&gt;&lt;br /&gt;С LINQ to SQL есть проблемы: вы не можете догрузить смежные таблицы используя один DataContext. Например у вас есть выборка по постам блога и вы хотите подтянуть авторов и коментарии, это можно сделать через DataLoadOptions. Но LoadOptions можно задать только один раз для одного экземпляра DataContext, в другом случае получите ошибку. Если вы используете DataContext атомарно для запроса, то проблем нет. Но если у вас DataContext создаете на один HttpContext - то провал. Как вариант, конкретно для методов где используется компилированый запрос, вы можете использовать DataContext атомарно.&lt;br /&gt;&lt;br /&gt;В LINQ to Entities эта проблема решена с помощью Include, где вы можете прям в запросе указать, какие таблицы вы хотите загрузить сразу, а не по требованию.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Полезные ссылки:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://msdn.microsoft.com/ru-ru/magazine/ee336024%28en-us%29.aspx"&gt;http://msdn.microsoft.com/ru-ru/magazine/ee336024%28en-us%29.aspx&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb399335.aspx"&gt;http://msdn.microsoft.com/en-us/library/bb399335.aspx&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-8175799370646960816?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/8175799370646960816/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2010/02/linq-precompiled-linq-queries.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/8175799370646960816'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/8175799370646960816'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2010/02/linq-precompiled-linq-queries.html' title='Улучшение производительности LINQ запросов. Precompiled LINQ Queries.'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-393008330255534829</id><published>2010-02-24T23:02:00.003+02:00</published><updated>2010-02-24T23:08:23.145+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web Services'/><title type='text'>Как получить параметры строки запроса в веб сервисе</title><content type='html'>Бывают ситуации, когда вы дергаете сервис с клиентской части и хотите сделать какие то вычисления учитывая параметры строки запроса, но в Request.QueryString всегда пусто. &lt;br /&gt;&lt;br /&gt;Допустим у вас есть следующий функционал: вы можете выбрать страницу и добавить на неё какие-то виджеты. Страница будет выглядеть так - Navigation.aspx?PageId=123. На странице будет js функция, которая дергает сервис для добавления выбранного виджета.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: true;"&gt;[WebService(Namespace = "http://tempuri.org/")]&lt;br /&gt;[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]&lt;br /&gt;[ScriptService]&lt;br /&gt;public class WidgetService : WebService&lt;br /&gt;{&lt;br /&gt;    [WebMethod]&lt;br /&gt;    public int AddWidget(int widgetId)&lt;br /&gt;    {&lt;br /&gt;        NameValueCollection request = HttpUtility.ParseQueryString(this.Context.Request.UrlReferrer.Query);&lt;br /&gt;        int pageId = int.Parse(request["PageId"]);&lt;br /&gt;&lt;br /&gt;        int widgetInstanceId = AddWidgetInternal(widgetId, pageId);&lt;br /&gt;&lt;br /&gt;        return widgetInstanceId;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Решение находится в строке #8. Чтоб получить строку запроса, её можно получить у страницы, с которой был вызван сервис.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-393008330255534829?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/393008330255534829/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2010/02/blog-post.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/393008330255534829'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/393008330255534829'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2010/02/blog-post.html' title='Как получить параметры строки запроса в веб сервисе'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-1065393428478592340</id><published>2010-01-22T11:33:00.000+02:00</published><updated>2010-01-22T11:33:33.209+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MS SQL 2008'/><title type='text'>Проблемы при подключении к удаленному MS SQL 2008 через SQLMS</title><content type='html'>Я поставил MS SQL 2008 Express (у меня стоит Vista) и&amp;nbsp;столкнулся с проблемой, что не могу посмотреть список баз данных под любой учеткой, хоть под windows authentication, хоть под sql authentication на удаленном сервере, если подключаюсь через Management Studio. Я всегда получаю сообщение &lt;a href="http://www.microsoft.com/products/ee/transform.aspx?ProdName=Microsoft+SQL+Server&amp;amp;ProdVer=10.00.2531&amp;amp;EvtSrc=MSSQLServer&amp;amp;EvtID=916" target="_blank"&gt;The server principal “%.*ls” is not able to access the database “%.*ls” under the current security context&lt;/a&gt;. Но если подключаюсь через Visual Studio, то все нормально.&lt;br /&gt;&lt;br /&gt;Проблема не в настройке удаленного сервера, а вашего локального. На XP по умолчанию SQL Server устанавливается и запускается от учетки Network Service, которую, как оказалось, Microsoft не рекомендует использовать для запуска SQL Server под Vista.&amp;nbsp;При установке эта опция стоит по умолчанию по старинке – Network Service (не понятно зачем?).&amp;nbsp;Я cтавил SQL Server с дефолтными настройками.&amp;nbsp;В этом и заключается вся проблема.&lt;br /&gt;&lt;br /&gt;Решение:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Если у вас стоит Vista или 7, то устанавливается MS SQL 2008 под учеткой LocalSystem или Build In Domain User. Если вы уже установили сервер, тогда в Sql Server Configuration Manager меняем учетку от которой будет запускаться SQL Server сервис на Local System&lt;/li&gt;&lt;li&gt;Если у вас стоит XP -&amp;nbsp;NetworkService.&lt;/li&gt;&lt;/ul&gt;Для более детальной информации:&amp;nbsp;&lt;a href="http://msdn.microsoft.com/ru-ru/library/ms143504%28SQL.90%29.aspx#Configure_services" target="_blank"&gt;Настройка служб, доступных в программе установки SQL Server&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/ms144259.aspx" target="_blank"&gt;Install SQL Server 2008 from the Command Prompt&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-1065393428478592340?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/1065393428478592340/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2010/01/ms-sql-2008-sqlms.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/1065393428478592340'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/1065393428478592340'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2010/01/ms-sql-2008-sqlms.html' title='Проблемы при подключении к удаленному MS SQL 2008 через SQLMS'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-1954228470897386031</id><published>2009-12-02T21:57:00.000+02:00</published><updated>2009-12-02T21:57:52.279+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>SQL Поиск по шаблону. PATINDEX</title><content type='html'>Недавно коллега с соседней команды подкинули мне интересную задачу: у вас алфавитное меню A-Z, при клике на букву в подменю должны отобразится записи, которые начинаются на выбранную букву. Заполнением подменю занимается аяксовый сервис, который дергает хранимку, которая должна вернуть данные наичнающиеся с указанной буквы(такая была архитектура). Меню строится для продуктов. Ниже пример названий продуктов.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_8B_3XCoKE6Q/Sxa7nQK60iI/AAAAAAAAAd4/yvDrpVKyC9w/s1600-h/SS-2009.12.02-21.07.58.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_8B_3XCoKE6Q/Sxa7nQK60iI/AAAAAAAAAd4/yvDrpVKyC9w/s640/SS-2009.12.02-21.07.58.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;Как видите не все так сладко. Некоторые продукты не начинаются с буквы. Допустим нам нужны продукты которые начинаются на 'А'. Должны отобразится 128, 130-135, 137, 138, 140 и тд. Я привел только те, которые видны на картинке. В базе 300 000 продуктов, а подменю должно отображатся как подменю :)&lt;br /&gt;Как решить задачу ?&lt;br /&gt;&lt;br /&gt;Решение 1:&lt;br /&gt;- Добавить колонку в таблицу, например [FirstLetter]. Вызвать один раз хранимку или серверный код, не важно, который заполнит это поле. Потому можно проиндексивать поле и выгребать данные как угодно и быстро. Или если товары импортируются, делать это при импорте.&lt;br /&gt;&lt;br /&gt;Решение 2:&lt;br /&gt;- Если "Решение 1" не подходит или нет возможности его реализовать, есть другой вариант. Можно выбрать один раз все имена продуктов на сервере, закешировать, поставить &lt;a href="http://msdn.microsoft.com/en-us/library/system.web.caching.sqlcachedependency.aspx"&gt;SqlCacheDependency&lt;/a&gt; если можно и нужно. Потом в сервисе можно в сохраненной выборке перебирать данные с помощью &lt;a href="http://msdn.microsoft.com/en-us/library/hs600312.aspx"&gt;Regex&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/bb308959.aspx"&gt;LINQ&lt;/a&gt;, искать первую букву, как угодно, возможностей много. Данное решение хорошее, но держать в кешэ 300 000 записей жирно та и требует изменений в существующей архитектуре - хочется сделать минимальные усилия с максимальным результатом исходя из того, что есть.&lt;br /&gt;&lt;br /&gt;Решение 3:&lt;br /&gt;- Хотелось бы воспользоваться оператором &lt;a href="http://msdn.microsoft.com/en-us/library/ms179859.aspx"&gt;LIKE&lt;/a&gt; в котором можно задать примитивный шаблон типо регулярки, но как я не крутил с LIKE, у меня ничего не получилось, но я был на правильном пути. И тогда я вспомнил что есть встроенная функция &lt;a href="http://msdn.microsoft.com/en-us/library/ms188395.aspx"&gt;PATINDEX&lt;/a&gt;, которая делает тоже самое, что и LIKE, только еще и возвращает индекс совпадения этого шаблона. По скольку нам нужно найти в названии первую букву и сравнить её с заданой, то это можно написать так (например для буквы 'A'):&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql; gutter: false;"&gt;SELECT [Name] AS 'Product Name'&lt;br /&gt;FROM Product&lt;br /&gt;WHERE SUBSTRING(LOWER([Name]), PATINDEX('%[a-z]%', LOWER([Name])), 1) = 'a'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;В результате получим:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_8B_3XCoKE6Q/SxbDnhSTE8I/AAAAAAAAAeA/ttE8Bd_hY-o/s1600-h/SS-2009.12.02-21.42.15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_8B_3XCoKE6Q/SxbDnhSTE8I/AAAAAAAAAeA/ttE8Bd_hY-o/s640/SS-2009.12.02-21.42.15.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;Данные выбираются меньше секунды. Задача решена как и хотелось, с минимальными усилиями и максимальным успехом.&lt;br /&gt;А теперь как это работает: функция PATINDEX ищет первое совпадение по указанному шаблону (с синтаксисом шаблона можно ознакомится по ссылкам). Наш шаблон говорит "искать первую попавщуюся букву в вырежении". Нашли, получили индекс этой буквы. Вырезаем эту букву функцией &lt;a href="http://msdn.microsoft.com/en-us/library/ms187748.aspx"&gt;SUBSTRING&lt;/a&gt; и сравниваем с заданной.&lt;br /&gt;Успехов.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-1954228470897386031?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/1954228470897386031/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2009/12/sql-patindex.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/1954228470897386031'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/1954228470897386031'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2009/12/sql-patindex.html' title='SQL Поиск по шаблону. PATINDEX'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Sxa7nQK60iI/AAAAAAAAAd4/yvDrpVKyC9w/s72-c/SS-2009.12.02-21.07.58.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-6943164294438768989</id><published>2009-11-08T01:17:00.006+02:00</published><updated>2009-11-08T14:01:47.763+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Bulk Operations'/><category scheme='http://www.blogger.com/atom/ns#' term='LINQ'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Performance'/><title type='text'>LINQ Bulk Insert</title><content type='html'>Недавно я столкнулся с проблемой произовдительности вставки данных с помощью L2S. Представьте себе следующую ситуацию: у нас e-commerce сайт, в котором можно создавать продукты, категории и мапить продукты на категорию (ии). Остановимся на функционале, где можно мапить продукты на категорию. У вас есть форма для редактирования категории, на которой в таблице отображаются продукты. Таблица пейжирована. Также есть 2 чекбокса - check\ unckeck all и check\unckeck current page ну и кнопка Save.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_8B_3XCoKE6Q/SvXpHUA9cPI/AAAAAAAAAdE/VwXDwL6K5ok/s1600-h/SS-2009.11.07-23.37.54.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_8B_3XCoKE6Q/SvXpHUA9cPI/AAAAAAAAAdE/VwXDwL6K5ok/s640/SS-2009.11.07-23.37.54.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Вы отмечаете check all нажимаете Save и &lt;strike&gt;жопа&lt;/strike&gt; ждете пока сохраниться мап всех товаров, которые у вас есть на категорию. Не важно большой у вас ассортимент или нет, это будет выполнятся долго, т.к. будет дергаться INSERT INTO CategoryToProductMap столько раз, сколько у вас товаров. В моем случаей это было 10 000 раз. Потом я встретил еще чудо-код, который выполнял подобные действия 130 000 раз ! Вы представляете это !? А если хотя бы 2 человека попадут на эту страницу...&lt;br /&gt;&lt;br /&gt;В данной статье мы поговорим о том как можно оптимзировать процесс вставки большого количества данных, большого значит &amp;gt; 1 000. Хотя вы сможете применять это по своим нуждам.&lt;br /&gt;&lt;br /&gt;В MS SQL 2000 появилась команда &lt;a href="http://msdn.microsoft.com/en-us/library/ms188365.aspx"&gt;BULK INSERT&lt;/a&gt;, которая возволяет загрузить данные из файла в SQL Server "пачкой". Внутри эта команда использует утилиту &lt;a href="http://msdn.microsoft.com/en-us/library/aa196743%28SQL.80%29.aspx"&gt;BCP&lt;/a&gt;, копирует данные из файла в указанную таблицу. В ADO.NET 2.0 появился класс &lt;a href="http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.aspx"&gt;SqlBulkCopy&lt;/a&gt;, который является обверткой над командой BULK INSERT. В его использовании нет ничего сложного. Вы указываете строку соединения, таблицу в которую хотите вставить данные и сами данные.&lt;br /&gt;Это нам подходит! Надо бы сделать это гламурненько в стиле L2S.&lt;br /&gt;Погуглив, я нашел пример как это делается в стиле L2S, но как по мне он сложноват как в исполнении так и в чтении. Если хотите, можете взять его, он рабочий, я проверил. Но! У меня получилось быстрей :). Почему - не знаю. В конце статьи я приведу примеры по скорости выполнения. &lt;br /&gt;Для реализации своего примера я почерпнул пару штрихов из источника, поэтому желательно если вы с ним ознакомитесь, хотя и необязательно. Примеры я буду приводить на всем известной Northwind базе данных. В результате у нас получится слудующее:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;IEnumerable&amp;lt;Customer&amp;gt; dataToInsert = GenerateCustomers(100000);&lt;br /&gt;int batchSize = 5000;&lt;br /&gt;context.Customers.BulkInsert(dataToInsert, batchSize);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Для того, чтобы сделать вставку, необходимо вызвать метод WriteToServer у класса &lt;a href="http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.aspx"&gt;SqlBulkCopy&lt;/a&gt;. У него есть несколько перегрузок:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_8B_3XCoKE6Q/SvXxfXeqvKI/AAAAAAAAAdM/UZySQ4l2tyo/s1600-h/SS-2009.11.08-00.12.22.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_8B_3XCoKE6Q/SvXxfXeqvKI/AAAAAAAAAdM/UZySQ4l2tyo/s640/SS-2009.11.08-00.12.22.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;мы будем использовать метод, который принимает IDataReader. У интерфейса IDataReader очень много методов, но нам необходимо только 4. Создадим класс реализующий этот интерфейс. Методы, которые нам нужны пометим как&amp;nbsp; &lt;b&gt;abstract&lt;/b&gt;, остальные как &lt;b&gt;virtual&lt;/b&gt;, они нам не нужны и я их не буду показывать в коде.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;public abstract class SqlBulkCopyReader : IDataReader&lt;br /&gt;{&lt;br /&gt;     public abstract bool Read();&lt;br /&gt;     public abstract object GetValue(int i);&lt;br /&gt;     public abstract int GetOrdinal(string name);&lt;br /&gt;     public abstract int FieldCount { get; }&lt;br /&gt;&lt;br /&gt;     #region // Not required ...&lt;br /&gt;     ....&lt;br /&gt;     #endregion &lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Отнаследуем класс от SqlBulkCopyReader:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: true;"&gt;public class LinqEntityDataReader&amp;lt;T&amp;gt; : SqlBulkCopyReader where T : class&lt;br /&gt;{&lt;br /&gt;     private DataTable _sourceTable;&lt;br /&gt;     private readonly IEnumeratort&amp;lt;T&amp;gt; _enumerator;&lt;br /&gt;     private DataRow _current;&lt;br /&gt;&lt;br /&gt;     public LinqEntityDataReader(IEnumerable&amp;lt;T&amp;gt; source)&lt;br /&gt;     {&lt;br /&gt;           MapTableName();&lt;br /&gt;           MapColumns();&lt;br /&gt;           _enumerator = source.GetEnumerator();&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;     public string DestenationTable &lt;br /&gt;     {&lt;br /&gt;          get { return _sourceTable.TableName; }&lt;br /&gt;     } &lt;br /&gt;&lt;br /&gt;     public IEnumerable&amp;lt;string&amp;gt; Columns&lt;br /&gt;     {&lt;br /&gt;          get&lt;br /&gt;          {&lt;br /&gt;               foreach (DataColumn column in _sourceTable.Columns)&lt;br /&gt;               {&lt;br /&gt;                    yield return column.ColumnName;&lt;br /&gt;               }&lt;br /&gt;           }&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      public override object GetValue(int columnIndex)&lt;br /&gt;      {&lt;br /&gt;           return _current[columnIndex];&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      public override int GetOrdinal(string name)&lt;br /&gt;      {&lt;br /&gt;           return _sourceTable.Columns[name].Ordinal;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      public override bool Read()&lt;br /&gt;      {&lt;br /&gt;           bool next = _enumerator.MoveNext();&lt;br /&gt;&lt;br /&gt;           if (next)&lt;br /&gt;           {&lt;br /&gt;                _current = _sourceTable.NewRow();&lt;br /&gt;&lt;br /&gt;                foreach (DataColumn column in _sourceTable.Columns)&lt;br /&gt;                {&lt;br /&gt;                     _current[column] = typeof(T).GetProperty(column.ColumnName)&lt;br /&gt;                           .GetValue(_enumerator.Current, null);&lt;br /&gt;                }&lt;br /&gt;           }&lt;br /&gt;&lt;br /&gt;           return next;&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;     public override int FieldCount&lt;br /&gt;     {&lt;br /&gt;          get { return _sourceTable.Columns.Count; }&lt;br /&gt;     } &lt;br /&gt;&lt;br /&gt;     private void MapTableName()&lt;br /&gt;     {&lt;br /&gt;          Type entityType = typeof(T);&lt;br /&gt;&lt;br /&gt;          TableAttribute destenationTable = entityType&lt;br /&gt;              .GetCustomAttributes(typeof(TableAttribute), false)&lt;br /&gt;              .Cast&amp;lt;TableAttribute&amp;gt;()&lt;br /&gt;              .SingleOrDefault();&lt;br /&gt;&lt;br /&gt;          if (destenationTable == null)&lt;br /&gt;          {&lt;br /&gt;               throw new ArgumentNullException("destenationTable");&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          _sourceTable = new DataTable(destenationTable.Name);&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;     private void MapColumns()&lt;br /&gt;     {&lt;br /&gt;          Type entityType = typeof(T);&lt;br /&gt;          int columnIndex = 0;&lt;br /&gt;&lt;br /&gt;          foreach (PropertyInfo property in entityType.GetProperties())&lt;br /&gt;          {&lt;br /&gt;               ColumnAttribute column = property&lt;br /&gt;                   .GetCustomAttributes(typeof(ColumnAttribute), false)&lt;br /&gt;                   .Cast&amp;lt;columnattribute&amp;gt;()&lt;br /&gt;                   .SingleOrDefault();&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          if (column == null)&lt;br /&gt;          {&lt;br /&gt;               return;&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          if (!column.IsVersion &amp;amp;&amp;amp; !column.DbType.Contains("IDENTITY") &amp;amp;&amp;amp; !column.IsDbGenerated)&lt;br /&gt;          {&lt;br /&gt;               _sourceTable.Columns.Add(column.Name ?? property.Name);&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          columnIndex++;&lt;br /&gt;     }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Начнем с конструктора. Для начала мы должны знать куда нам вставлять данные. Это мы можем узнать у сущности из аттрибута Table, что и происходит в методе MapTableName. Затем нам нужно знать в какие колонки можно вставлять данные. Во вставке могу участвовать лубые колонки кроме IDENTITY, TIMESTAMP и тп. Это можно узнать у свойства сущности, которое помечено аттрибутом Column, что и происходит в методе MapColumns.&lt;br /&gt;SqlBulkCopy начинает свою работу с получения количества колонок, которые участвую во вставке и если указан мапинг, то вызывается метод GetOrdinal, чтоб получить индекс каждой колонки по имени.&lt;br /&gt;Затем SqlBulkCopy идет построчно вызывая метод Read, а потом вызывает GetValue для каждой колонки у текущей записи. &lt;br /&gt;Мое "ноу-хау" - это получение значений колонок рефлексией, когда запрашивается очередная строка, и сохранение этих значений в DataRow. Во-первых это проще, во-вторых читабельней и понятней, а в-третьих - думаете рефлексия в методе Read будет все тормозить ? - Нет! Получилось только быстрей, на удивление (может кто-то скажет почему ?).&lt;br /&gt;&lt;br /&gt;Ну и завершающий этап, сама обвертка над SqlBulkCopy в виде Extenstion Method для таблицы-сущности:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;public static class TableExtension&lt;br /&gt;{&lt;br /&gt; public static void BulkInsert&amp;lt;T&amp;gt;(this Table&amp;lt;T&amp;gt; entity, IEnumerable&amp;lt;T&amp;gt; data, int batchSize) &lt;br /&gt;  where T : class&lt;br /&gt; {&lt;br /&gt;  LinqEntityDataReader&amp;lt;T&amp;gt; reader = new LinqEntityDataReader&amp;lt;T&amp;gt;(data);&lt;br /&gt;&lt;br /&gt;  using (SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(DBContext.ConnectionString))&lt;br /&gt;  {&lt;br /&gt;   foreach (string columnName in reader.Columns)&lt;br /&gt;   {&lt;br /&gt;    sqlBulkCopy.ColumnMappings.Add(columnName, columnName);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   sqlBulkCopy.BatchSize = batchSize;&lt;br /&gt;   sqlBulkCopy.DestinationTableName = reader.DestenationTable;&lt;br /&gt;   sqlBulkCopy.WriteToServer(new LinqEntityDataReader&amp;lt;T&amp;gt;(data));&lt;br /&gt;   sqlBulkCopy.Close();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ну тут вроде ничего военного, все должно быть ясно. Ну и в результате:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;IEnumerable&amp;lt;Customer&amp;gt; dataToInsert = GenerateCustomers(100000);&lt;br /&gt;int batchSize = 5000;&lt;br /&gt;context.Customers.BulkInsert(dataToInsert, batchSize);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ну и как я обещал грубый тест на производительность. Тест производился на вставке 100 000 записей. BatchSize я подобрал оптимальную для данного количества записей. Забыл сказать, что от этого значения зависит много, вы можете как выиграть в производительности, так и не очень выиграть. Для того чтобы подобрать оптимальное значение надо поэксперементировать. Рекомендации мелкософта не помогли, все зависит от ситуации. Для создания фейковых данных использовался метод:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;public static IEnumerable&amp;lt;Customer&amp;gt; GenerateCustomers(int count)&lt;br /&gt;{&lt;br /&gt; for (int i = 0; i &amp;lt; count; i++)&lt;br /&gt; {&lt;br /&gt;  yield return new Customer&lt;br /&gt;              {&lt;br /&gt;           CustomerID = i.ToString("00000"), &lt;br /&gt;   CompanyName = "Company" + i, &lt;br /&gt;   ContactName = "Frederique Citeaux",&lt;br /&gt;                        ContactTitle = "Marketing Manager",&lt;br /&gt;   Address = "24, place Kleber",&lt;br /&gt;   Phone = "(604) 555-4729",&lt;br /&gt;   Region = "WA"&lt;br /&gt;         };&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Вот что у меня получилось:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Мое решение ~ 8 секунд&lt;/li&gt;&lt;li&gt;Решение из пример ~ 11секунд&lt;/li&gt;&lt;li&gt;Решение из пример + динамический вызов GetValue, который ему посоветовали в коментах ~ 9.5 секунд&lt;/li&gt;&lt;li&gt;Стандартный InsertAllOnSubmit ~ 1 минута 23 секунды &lt;/li&gt;&lt;/ul&gt;Я не думаю, что это совпадение, все-таки 100 000 данных - это немало. Почему так, я еще не узнал.&lt;br /&gt;В любой случае я получил, что хотел. В моем конкретном случае мапинг всех товаров на категорию (10 000 записей) теперь выполняется ~ 1.1 секунды. Думаю этот пример вас когда-то спасет.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-6943164294438768989?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/6943164294438768989/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2009/11/linq-bulk-insert.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/6943164294438768989'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/6943164294438768989'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2009/11/linq-bulk-insert.html' title='LINQ Bulk Insert'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_8B_3XCoKE6Q/SvXpHUA9cPI/AAAAAAAAAdE/VwXDwL6K5ok/s72-c/SS-2009.11.07-23.37.54.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-3094320118544768074</id><published>2009-11-07T17:17:00.003+02:00</published><updated>2009-11-10T11:32:55.671+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Expression Trees'/><category scheme='http://www.blogger.com/atom/ns#' term='LINQ'/><title type='text'>LINQ dynamic expressions. WHERE &lt;T&gt; IN &lt;Filters&gt;. Part2 - Improvements</title><content type='html'>В &lt;a href="http://delmadman.blogspot.com/2009/11/linq-dynamic-expressions-where-in.html"&gt;предыдущей&lt;/a&gt; статье мы рассмотрели как можно создать аналог SQL опператора WHERE IN.&lt;br /&gt;В результате мы получили LINQ выражение в вид: &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;string[] filters = new [] { "WA", "SP", "BC" };&lt;br /&gt;var customers = context.Customers.In(p =&amp;gt; p.Region, filters);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;В читабильности мы не достигли идеального результат, но &lt;a href="http://restuta.blogspot.com/"&gt;Restuta&lt;/a&gt; предложил решение, как можно его улучшить.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Благодарности:&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Спасибо &lt;a href="http://restuta.blogspot.com/"&gt;Restuta&lt;/a&gt; за предложенное улучшение.&lt;br /&gt;&lt;br /&gt;Рассмотри что изменилось.&lt;br /&gt;&lt;br /&gt;Для начала, нам необходимо создать вспомогательный класс, который, как сказал Restuta, надо "пропихнуть"... Наш класс будет выглядеть так:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;public class QueryableProperty&amp;lt;T, TKey&amp;gt;&lt;br /&gt;{&lt;br /&gt;     /// &amp;lt;summary&amp;gt;Gets or sets the original query.&amp;lt;/summary&amp;gt;&lt;br /&gt;     /// &amp;lt;value&amp;gt;The query.&amp;lt;/value&amp;gt;&lt;br /&gt;     public IQueryable&amp;lt;T&amp;gt; Query { get; set; }&lt;br /&gt;&lt;br /&gt;     /// &amp;lt;summary&amp;gt;Gets or sets the column to filter.&amp;lt;/summary&amp;gt;&lt;br /&gt;     /// &amp;lt;value&amp;gt;The column.&amp;lt;/value&amp;gt;&lt;br /&gt;     public Expression&amp;lt;Func&amp;lt;T, TKey&amp;gt;&amp;gt; Column { get; set; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Затем нам необходим вспомогательный Extenstion Method, который как раз и будет "пропихивать" поле, которое мы хотим фильтровать и оригинальный запрос. Этот Extenstion Method как раз и является тем самым улучшением, который придает желаемой читабельности.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;/// &amp;lt;summary&amp;gt;Aggregates property for future WHERE IN clause.&amp;lt;/summary&amp;gt;&lt;br /&gt;/// &amp;lt;typeparam name="T"&amp;gt;The result entity type.&amp;lt;/typeparam&amp;gt;&lt;br /&gt;/// &amp;lt;typeparam name="TKey"&amp;gt;The type of column to filter.&amp;lt;/typeparam&amp;gt;&lt;br /&gt;/// &amp;lt;param name="query" /&amp;gt;The original query.&amp;lt;/param&amp;gt;&lt;br /&gt;/// &amp;lt;param name="column" /&amp;gt;The column to filter.&amp;lt;/param&amp;gt;&lt;br /&gt;public static QueryableProperty&amp;lt;T, TKey&amp;gt; WhereProperty&amp;lt;T, TKey&amp;gt;(this IQueryable&amp;lt;T&amp;gt; query, Expression&amp;lt;Func&amp;lt;T, TKey&amp;gt;&amp;gt; column)&lt;br /&gt;{&lt;br /&gt;     return new QueryableProperty&amp;lt;T, TKey&amp;gt; { Query = query, Column = column };&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Extenstion Method 'IN' почти не изменился, я не буду приводить его код, покажу только сигнатуру, по которой все станет ясно.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;public static IQueryable&amp;lt;T&amp;gt; In&amp;lt;T, TKey&amp;gt;(this QueryableProperty&amp;lt;T, TKey&amp;gt; queryableProperty, IEnumerable&amp;lt;TKey&amp;gt; filters)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;В результате, наше вырежание приняло следующий вид:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;string[] filters = new [] { "WA", "SP", "BC" };&lt;br /&gt;var customers = context.Customers&lt;br /&gt;     .WhereProperty(p =&amp;gt; p.Region)&lt;br /&gt;     .In(filters)&lt;br /&gt;     .OrderBy(p =&amp;gt; p.Region);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Получилось гламурненько. Как вы видите, цепочка вызовов не прерывается. Это самое главное. После In мы опять возвращаем IQueryable&amp;lt;T&amp;gt; и можем продолжать использовать другие Extension Methods.&lt;br /&gt;Мы, конечно, можем обратиться к оригинальному запросу и после WhereProperty через свойство Query, но WhereProperty предназначался не для того.&lt;br /&gt;&lt;br /&gt;Мы добились желаемого результат \m/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-3094320118544768074?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/3094320118544768074/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2009/11/linq-dynamic-expressions-where-in-part2.html#comment-form' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/3094320118544768074'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/3094320118544768074'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2009/11/linq-dynamic-expressions-where-in-part2.html' title='LINQ dynamic expressions. WHERE &amp;lt;T&amp;gt; IN &amp;lt;Filters&amp;gt;. Part2 - Improvements'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-209848624506015632</id><published>2009-11-01T21:18:00.008+02:00</published><updated>2009-11-01T23:06:25.818+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Expression Trees'/><category scheme='http://www.blogger.com/atom/ns#' term='LINQ'/><title type='text'>LINQ dynamic expressions. WHERE &lt;T&gt; IN &lt;Filters&gt;</title><content type='html'>&lt;p&gt;В LINQ есть конструкция, которая позволяет делать SQL запрос с выражением IN. Все примеры я буду приводить на всем известной базе данных Northwind. &lt;/p&gt;&lt;p&gt;Например чтоб получить запрос на SQL в виде&lt;/p&gt;&lt;br /&gt;&lt;pre class="brush: sql; gutter: false;"&gt;SELECT c.* &lt;br /&gt;FROM Customers c&lt;br /&gt;WHERE c.Region IN ("WA", "SP", "BC")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;нужно написать на LINQ следующий код&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;string[] filters = new [] { "WA", "SP", "BC" };&lt;br /&gt;context.Customers.Where(p =&amp;gt; filters.Contains(p.Region));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;Данная конструкция нечитабельная, лично меня она напрягает. Мне хотелось бы чтоб она выглядела более естественно, как то так:&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;string[] filters = new [] { "WA", "SP", "BC" };&lt;br /&gt;context.Customers.Where(p =&amp;gt; p.Region.In(filters));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;Конечно так написать нельзя и данных код нескомпилируется, но сделать запрос более естественным можно. Нам помогут Expression Trees, примеры которых вы могли видеть в предыдущих статьях. Далее будет рассказано, как сделать код вида:&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;string[] filters = new [] { "WA", "SP", "BC" };&lt;br /&gt;var customers = context.Customers.In(p =&gt; p.Region, filters);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;Погуглив, я нашел пример в котором делается &lt;a href="http://george.tsiokos.com/posts/2007/11/30/linq-where-x-in/" target="_blank"&gt;примерно тоже самое&lt;/a&gt;, только проще и с обманом. В моем случае, на выходе мы получим SQL запрос с конструкцией &lt;strong&gt;WHERE IN&lt;/strong&gt;, а в примере получается … AND (c.Region = “WA” &lt;strong&gt;OR&lt;/strong&gt; c.Region = “SP” …). Я думаю парень не выдержал, задача все-таки нелегкая. Я тоже понервничал пока разобрался как транслировать в конструкцию &lt;strong&gt;IN&lt;/strong&gt;. Вам будет проще :)&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Начнем. &lt;/div&gt;&lt;div&gt;Ниже приведен код “In” Extenstion method’a и подробное его описание по шагам.&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: true;"&gt;public static class LinqExtensions&lt;br /&gt;{&lt;br /&gt;     /// &amp;lt;summary&amp;gt;&lt;br /&gt;     /// Filters the specified query with input filters.&lt;br /&gt;     /// &amp;lt;/summary&amp;gt;&lt;br /&gt;     /// &amp;lt;typeparam name="T"&amp;gt;The result entity type.&amp;lt;/typeparam&amp;gt;&lt;br /&gt;     /// &amp;lt;typeparam name="TKey"&amp;gt;The type of column to filter.&amp;lt;/typeparam&amp;gt;&lt;br /&gt;     /// &amp;lt;param name="query" /&amp;gt;The query.&amp;lt;/param&amp;gt;&lt;br /&gt;     /// &amp;lt;param name="column" /&amp;gt;The column.&amp;lt;/param&amp;gt;&lt;br /&gt;     /// &amp;lt;param name="filters" /&amp;gt;The in sequence.&amp;lt;/param&amp;gt;&lt;br /&gt;     public static IQueryable&amp;lt;T&amp;gt; In&amp;lt;T, TKey&amp;gt;(&lt;br /&gt;          this IQueryable&amp;lt;T&amp;gt; query, &lt;br /&gt;          Expression&amp;lt;Func&amp;lt;T, TKey&amp;gt;&amp;gt; column,&lt;br /&gt;          IEnumerable&amp;lt;TKey&amp;gt; filters)&lt;br /&gt;     {&lt;br /&gt;          MethodInfo containsMethod = typeof(System.Linq.Enumerable)&lt;br /&gt;               .GetMethods()&lt;br /&gt;               .Where(m =&amp;gt; m.Name == "Contains" &amp;&amp; m.GetParameters().Length == 2)&lt;br /&gt;               .Single();&lt;br /&gt;&lt;br /&gt;          containsMethod = containsMethod.MakeGenericMethod(new[] { typeof(TKey) });&lt;br /&gt;&lt;br /&gt;          List&amp;lt;expression&amp;gt; values = new List&amp;lt;expression&amp;gt;();&lt;br /&gt;          foreach (TKey value in filters)&lt;br /&gt;          {&lt;br /&gt;               values.Add(Expression.Constant(value));&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          NewArrayExpression filtersArray = Expression.NewArrayInit(typeof(TKey), values);&lt;br /&gt;&lt;br /&gt;          List&amp;lt;expression&amp;gt; arguments = new List&amp;lt;expression&amp;gt;();&lt;br /&gt;          arguments.Add(filtersArray);&lt;br /&gt;          arguments.Add(column.Body);&lt;br /&gt;&lt;br /&gt;          MethodCallExpression containsMethodCall = Expression.Call(containsMethod, arguments.ToArray());&lt;br /&gt;&lt;br /&gt;          LambdaExpression lambda = Expression.Lambda(containsMethodCall, column.Parameters.ToArray());&lt;br /&gt;&lt;br /&gt;          var result = Expression.Call(&lt;br /&gt;               typeof(Queryable),&lt;br /&gt;               "Where",&lt;br /&gt;               new Type[] { query.ElementType },&lt;br /&gt;               query.Expression,&lt;br /&gt;               lambda);&lt;br /&gt;&lt;br /&gt;          return query.Provider.CreateQuery&amp;lt;T&amp;gt;(result);&lt;br /&gt;     }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;Рассмотрим подробней:&lt;/div&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Сначала нам надо найти метод &lt;strong&gt;Contains &lt;/strong&gt;(17-20). Поскольку этот метод является Extenstion Method, то его нужно искать в классе, в котором хранятся эти расширения. В данном случае &lt;strong&gt;System.Linq.Enumerable.&lt;/strong&gt; Методов &lt;strong&gt;Contains&lt;/strong&gt; два: &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: true;"&gt;Contains&amp;gt;TSource&amp;lt;(IEnumerable&amp;gt;TSource&amp;lt;, TSource)&lt;br /&gt;Contains&amp;gt;TSource&amp;lt;(IEnumerable&amp;gt;TSource&amp;lt;, TSource, IEqualityComparer&amp;gt;TSource&amp;lt;) &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;нам нужен [1] с двумя параметрами.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;После того как мы его нашли, у нас будет нетипизированный метод в виде &lt;strong&gt;Contains[TSource]&lt;/strong&gt;, а нам нужен &lt;strong&gt;Contains&lt;/strong&gt;, который работает с типом поля, которое нам передается. В данном примере мы фильтровали по &lt;strong&gt;Region&lt;/strong&gt; у которого тип &lt;strong&gt;System.String&lt;/strong&gt;. Делаем с общего метода, типизированный (23). В результате получаем сигнатуру вида &lt;strong&gt;Contains&amp;lt;String&amp;gt;(&amp;lt;IEnumerable&amp;lt;String&amp;gt;, String&amp;gt;)&lt;/strong&gt;.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Затем нам нужен список значений, по которым будет фильтроваться выборка. Для этого создаем набор констант (24-28) и помещаем их в массив (30). В результате мы получаем динамическое выражение вида &lt;strong&gt;new[] { “WA”, “BC”, “SP”}&lt;/strong&gt;.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Согласно сигнатуре метода &lt;strong&gt;Contains&lt;/strong&gt; [1] мы должны передать в него набор фильтров (33) и параметр-поле (34), по которым будет фильтроваться выборка. Делаем вызов метода &lt;strong&gt;Contains&lt;/strong&gt; (36). В результате мы получаем динамическое выражение вида &lt;strong&gt;new[] { “WA”, “BC”, “SP”}.Contains(p.Region)&lt;/strong&gt;. Тут мы как раз делаем саму &lt;strong&gt;IN&lt;/strong&gt; конструкцию.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;После этого нам нужно создать лямбда выражение (38), которое будет передаваться в конструкцию &lt;strong&gt;Where&lt;/strong&gt;. В результате мы получаем динамическое выражение вида &lt;strong&gt;p =&amp;gt; new[] { “WA”, “BC”, “SP”}.Contains(p.Region)&lt;/strong&gt;. Входящий параметр лямбда выражения будет таким, как вы его назовете при использовании нашего “In” Exstension method. В данном примере он называется p, т.к. я делал вызов вида &lt;strong&gt;context.Customers.In(p =&amp;gt; p.Region, filters)&lt;/strong&gt;.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Все. Нам осталось добавить в существующую цепочку вызовов наш метод &lt;strong&gt;Where &lt;/strong&gt;(40-45).&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Думаю вы почерпнулия для себя что-то новое и полезное. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-209848624506015632?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/209848624506015632/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2009/11/linq-dynamic-expressions-where-in.html#comment-form' title='Комментарии: 7'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/209848624506015632'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/209848624506015632'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2009/11/linq-dynamic-expressions-where-in.html' title='LINQ dynamic expressions. WHERE &amp;lt;T&amp;gt; IN &amp;lt;Filters&amp;gt;'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-1649842228736012563</id><published>2009-08-21T19:16:00.005+03:00</published><updated>2009-11-08T02:34:48.601+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WPF'/><category scheme='http://www.blogger.com/atom/ns#' term='Binding'/><title type='text'>Editable Text с использование IValueConverter</title><content type='html'>Сегодня я хотел бы рассказать об использование &lt;a href="http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.aspx"&gt;IValueConverter&lt;/a&gt; интерфейсе и как используют ValueConvertor’ы в WPF.&lt;br /&gt;&lt;br /&gt;ValueConvertor’ы очень полезная и удобная штука, которая очень часто используется при &lt;a href="http://msdn.microsoft.com/en-us/library/ms752347.aspx"&gt;биндинге&lt;/a&gt; (будем по народному)для преобразования одного типа данных в другой, например:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Вы делаете компонент который отображает прогресс какого-то действия и вам надо менять цвет от зеленого до красного, в зависимости от того, на сколько прогресс движется к концу;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Вам надо сделать какой-то расчет, например зарплату по количеству часов * рейт работника;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Или более простые пример, это отформатировать дату или преобразовать цвет по #AABBCCDD в строковое представление;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Инвертировать отображения элемента, например у вас биндится свойство на CheckBox типо IsDeclined и если оно true, то CheckBox не должен быть отмечен (пример не надуманый, всякое бывает). Если есть возможность, то можно переименовать в Allowed и будет логично, а если нет? Тогда делаем сами уже знаете что.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p align="justify"&gt;Конверторы инкапсулируют какую-то логику, которая может меняться, они также могут принимать параметры. Также конверторы используются для отладки биндинга (об этом позже).&lt;/p&gt;&lt;p align="justify"&gt;Я думаю у многих возникала потребность в редактируемом тексте, например отображается текст в метке “Edit me”, вы щелкаете по тексту, он меняется на TextBox, вы его редактируете, нажимаете Enter и опять метка, круто.&lt;/p&gt;&lt;p align="justify"&gt;Рассмотрим как это можно сделать с использованием конверторов. Определим правила игры:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Метка может иметь начальный текст&lt;/li&gt;&lt;li&gt;Метка переходит в состояние редактирования по щелчку мышки на ней&lt;/li&gt;&lt;li&gt;TextBox переходит в состояние метки после нажатия клавиши Enter или когда пользователь уводит курсор с TextBox или теряется фокус.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Рассмотрим XAML нашего компонента:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: xml; gutter: false;"&gt;&amp;lt;UserControl x:Class="EditableLabel.Control.EditableText"&lt;br /&gt;     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"&lt;br /&gt;     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&lt;br /&gt;     xmlns:Converters="clr-namespace:EditableLabel.Converters"    &lt;br /&gt;     x:Name="editableText"&amp;gt;&lt;br /&gt;     &amp;lt;UserControl.Resources&amp;gt;&lt;br /&gt;          &amp;lt;Converters:BooleanToVisibleConverter x:Key="converter" /&amp;gt;&lt;br /&gt;     &amp;lt;/UserControl.Resources&amp;gt;    &lt;br /&gt;     &amp;lt;Grid&amp;gt;&lt;br /&gt;          &amp;lt;Label x:Name="label" &lt;br /&gt;               Content="{Binding ElementName=editableTextBox, Path=Text}" &lt;br /&gt;               MouseLeftButtonDown="OnMouseLeftButtonClick"&lt;br /&gt;               Visibility="{Binding AllowEdit, ElementName=editableText,&lt;br /&gt;               Converter={StaticResource converter}, ConverterParameter=True}"/&amp;gt;&lt;br /&gt;&lt;br /&gt;          &amp;lt;TextBox x:Name="editableTextBox" &lt;br /&gt;               LostFocus="OnTextBoxLostFocus" &lt;br /&gt;               MouseLeave="OnTextBoxMouseLeave" &lt;br /&gt;               KeyDown="OnTextBoxKeyDown" &lt;br /&gt;               Text="{Binding Text, ElementName=editableText}"&lt;br /&gt;               Visibility="{Binding AllowEdit, ElementName=editableText,&lt;br /&gt;               Converter={StaticResource converter}, ConverterParameter=False}"/&amp;gt;&lt;br /&gt;     &amp;lt;/Grid&amp;gt;&lt;br /&gt;&amp;lt;/UserControl&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div align="justify"&gt;У нас есть Label и TextBox. Когда AllowEdit свойство True отображается TextBox, в другом случае Label.&lt;/div&gt;&lt;br /&gt;&lt;div align="justify"&gt;Текст для TextBox берется из свойства Text компонента, а текст для метки берется из TextBox. Зависимость выглядит так:&lt;/div&gt;&lt;br /&gt;&lt;div align="justify"&gt;&amp;#160;&amp;#160;&amp;#160; Label.Text &amp;lt; –&amp;#160; TextBox.Text &amp;lt; – &amp;gt; UserControl.Text&lt;/div&gt;&lt;br /&gt;&lt;div align="justify"&gt;В ресурсах у нас объявлен конвертер, который используется для свойства &lt;strong&gt;Visibility&lt;/strong&gt; у Label и TextBox.&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;Visibility="{Binding AllowEdit, ElementName=editableText,&lt;br /&gt;Converter={StaticResource converter}, ConverterParameter=False}&lt;br /&gt;&lt;/pre&gt;вот его код:&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;namespace EditableLabel.Converters&lt;br /&gt;{&lt;br /&gt;     [ValueConversion(typeof(bool), typeof(Visibility))]&lt;br /&gt;     public class BooleanToVisibleConverter : IValueConverter&lt;br /&gt;     {&lt;br /&gt;          public object Convert(object value, Type targetType, object parameter, CultureInfo culture)&lt;br /&gt;          {&lt;br /&gt;               bool flag = (bool) value;&lt;br /&gt;               bool inverted = false;&lt;br /&gt;&lt;br /&gt;               if (parameter != null)&lt;br /&gt;               {&lt;br /&gt;                    inverted = Boolean.Parse(parameter.ToString());&lt;br /&gt;               }&lt;br /&gt;&lt;br /&gt;               return inverted ? &lt;br /&gt;                    (flag ? Visibility.Hidden : Visibility.Visible) &lt;br /&gt;                    : (flag ? Visibility.Visible : Visibility.Hidden);&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)&lt;br /&gt;          {&lt;br /&gt;               throw new NotImplementedException();&lt;br /&gt;          }&lt;br /&gt;     }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Метод &lt;strong&gt;ConvertBack&lt;/strong&gt; очень редко используется, в некоторых случаях при двунаправленном биндинге (расскажу в другой раз), поэтому всегда остается таким и не вызывается в данном случае.&lt;br /&gt;&lt;br /&gt;&lt;div align="justify"&gt;В метод &lt;strong&gt;Convert&lt;/strong&gt;, в параметр &lt;strong&gt;value&lt;/strong&gt; приходит значение, которое мы биндим, в данном случае &lt;strong&gt;AllowEdit&lt;/strong&gt;, а в &lt;strong&gt;parameter&lt;/strong&gt; будет передано значение, которое мы указали в &lt;strong&gt;ConverterParameter &lt;/strong&gt;(если не указали - &lt;strong&gt;null&lt;/strong&gt; соответственно).&lt;/div&gt;&lt;br /&gt;&lt;div align="justify"&gt;&lt;strong&gt;ConverterParameter&lt;/strong&gt; не единственный способ передать параметры в класс конвертора. Если вам надо передать несколько параметров это можно сделать так:&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;[ValueConversion(typeof(bool), typeof(Visibility))]&lt;br /&gt;public class BooleanToVisibleConverter : IValueConverter&lt;br /&gt;{&lt;br /&gt;     public string AdditionalValue { get; set; }&lt;br /&gt;&lt;br /&gt;     public int AnotherValue { get; set; }&lt;br /&gt;&lt;br /&gt;     public object Convert(object value, Type targetType, object parameter, CultureInfo culture)&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;и затем использовать в разметке или в коде так:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: xml; gutter: false;"&gt;&amp;lt;Converters:BooleanToVisibleConverter &lt;br /&gt;     x:Key="converter" &lt;br /&gt;     AdditionalValue="asd" &lt;br /&gt;     AnotherValue="2" /&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div align="justify"&gt;Единственная проблема, если у вас биндятся ListBoxItem’s, например, и у каждого свое значение для конвертора, то такой способ объявления конвертора со свойствами не прокатит. Этот способ хорошо когда конвертор используется 1 раз. В другом случае придется использовать &lt;a href="&amp;lt;Converters:BooleanToVisibleConverter "&gt;IMultiValueConverter&lt;/a&gt;. Принцип тот же самый, только в &lt;strong&gt;Convert &lt;/strong&gt;передается &lt;strong&gt;object[] values.&lt;/strong&gt;&lt;/div&gt;&lt;br /&gt;&lt;div align="justify"&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.windows.data.valueconversionattribute.aspx"&gt;ValueConversion&lt;/a&gt; атрибут используется для того, чтоб быстрей было понять, из какого в какой тип происходит конвертация. Этот атрибут не обязателен, просто best practice + удобство чтения.&lt;/div&gt;&lt;br /&gt;Код событий:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;using System.Windows;&lt;br /&gt;using System.Windows.Controls;&lt;br /&gt;using System.Windows.Input;&lt;br /&gt;&lt;br /&gt;namespace EditableLabel.Control&lt;br /&gt;{&lt;br /&gt;    /// &amp;lt;summary&amp;gt;&lt;br /&gt;    /// Interaction logic for EditableText.xaml&lt;br /&gt;    /// &amp;lt;/summary&amp;gt;&lt;br /&gt;    public partial class EditableText : UserControl&lt;br /&gt;    {&lt;br /&gt;        public static readonly DependencyProperty TextProperty =&lt;br /&gt;            DependencyProperty.Register("Text", typeof (string), typeof (EditableText));&lt;br /&gt;&lt;br /&gt;        public static readonly DependencyProperty AllowEditProperty =&lt;br /&gt;            DependencyProperty.Register("AllowEdit", typeof (bool), typeof (EditableText));&lt;br /&gt;&lt;br /&gt;        public bool AllowEdit&lt;br /&gt;        {&lt;br /&gt;            get { return (bool) GetValue(AllowEditProperty); }&lt;br /&gt;            set { SetValue(AllowEditProperty, value); }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public string Text&lt;br /&gt;        {&lt;br /&gt;            get { return (string) GetValue(TextProperty); }&lt;br /&gt;            set { SetValue(TextProperty, value); }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public EditableText()&lt;br /&gt;        {&lt;br /&gt;            InitializeComponent();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private void OnMouseLeftButtonClick(object sender, MouseButtonEventArgs e)&lt;br /&gt;        {&lt;br /&gt;            AllowEdit = true;&lt;br /&gt;            editableTextBox.Focus();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private void OnTextBoxLostFocus(object sender, RoutedEventArgs e)&lt;br /&gt;        {&lt;br /&gt;            AllowEdit = false;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private void OnTextBoxMouseLeave(object sender, MouseEventArgs e)&lt;br /&gt;        {&lt;br /&gt;            AllowEdit = false;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private void OnTextBoxKeyDown(object sender, KeyEventArgs e)&lt;br /&gt;        {&lt;br /&gt;            if (e.Key == Key.Enter)&lt;br /&gt;            {&lt;br /&gt;                AllowEdit = false;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;strong&gt;Варианты для усовершенствования:&lt;/strong&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Cделать чтоб ширна TextBox’a была такой же как у метки;&lt;/li&gt;&lt;li&gt;Возможно вынести в отдельную сборку для дальнейшего использования и сделать как Custom Control, может потом захочется вид изменить или эффекты прикрутить.&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-1649842228736012563?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/1649842228736012563/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2009/08/editable-text-ivalueconverter.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/1649842228736012563'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/1649842228736012563'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2009/08/editable-text-ivalueconverter.html' title='Editable Text с использование IValueConverter'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-6097727493889589047</id><published>2009-07-28T17:19:00.009+03:00</published><updated>2009-11-08T02:08:50.745+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Expression Trees'/><category scheme='http://www.blogger.com/atom/ns#' term='LINQ'/><title type='text'>LINQ dynamic expressions. Where</title><content type='html'>&lt;div style="text-align: justify;"&gt;Я как-то писал пример, как можно &lt;a href="http://delmadman.blogspot.com/2009/04/linq-dynamic-expressions-orderby.html"&gt;создать динамическое выражения для сортировки&lt;/a&gt;. Сегодня мы рассмотрим еще один пример, как можно написать динамическое выражения для фильтрации по конкретному полю.&lt;br /&gt;&lt;br /&gt;Здесь выполняются те же действия, что и в OrderBy, только добавилось еще обращение к значению фильтра 'value'. Пример упрощен тем, что для фильтрации предполагается использование оператора '='.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;public static class WhereExtension&lt;br /&gt;{&lt;br /&gt;     /// &amp;lt;summary&amp;gt;Filters input sequence with specified value.&amp;lt;/summary&amp;gt;&lt;br /&gt;     /// &amp;lt;param name="query"&amp;gt;The query.&amp;lt;/param&amp;gt;&lt;br /&gt;     /// &amp;lt;param name="column"&amp;gt;The column name to filter.&amp;lt;/param&amp;gt;&lt;br /&gt;     /// &amp;lt;param name="value"&amp;gt;The value to filter with.&amp;lt;/param&amp;gt;&lt;br /&gt;     public static IQueryable&amp;lt;T&amp;gt; Where&amp;lt;T&amp;gt;(this IQueryable&amp;lt;T&amp;gt; query, string column, object value)&lt;br /&gt;     {&lt;br /&gt;          if (string.IsNullOrEmpty(column))&lt;br /&gt;          {&lt;br /&gt;               return query;&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          // Where(p =&amp;gt; p.{Column} == {value})&lt;br /&gt;          PropertyInfo property = query.ElementType.GetProperty(column);&lt;br /&gt;          ParameterExpression parameter = Expression.Parameter(query.ElementType, "p");&lt;br /&gt;          MemberExpression memberAccess = Expression.MakeMemberAccess(parameter, property);&lt;br /&gt;          ConstantExpression filter = Expression.Constant(value);&lt;br /&gt;          BinaryExpression condition = Expression.Equal(memberAccess, filter);&lt;br /&gt;          LambdaExpression lambda = Expression.Lambda(condition, parameter);&lt;br /&gt;&lt;br /&gt;          MethodCallExpression result = Expression.Call(&lt;br /&gt;               typeof(Queryable), "Where",&lt;br /&gt;               new [] { query.ElementType},&lt;br /&gt;               query.Expression, &lt;br /&gt;               lambda);&lt;br /&gt;&lt;br /&gt;          return query.Provider.CreateQuery&amp;lt;T&amp;gt;(result);&lt;br /&gt;     }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Разобраться не сложно.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Возможности для усовершенствования:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Расширить операторы фильтрации, например '&amp;gt;', '&amp;lt;', '&amp;lt;&amp;gt;' и тд.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-6097727493889589047?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/6097727493889589047/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2009/07/linq-dynamic-expressions-where.html#comment-form' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/6097727493889589047'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/6097727493889589047'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2009/07/linq-dynamic-expressions-where.html' title='LINQ dynamic expressions. Where'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-4671038586236281269</id><published>2009-04-28T21:42:00.003+03:00</published><updated>2009-11-08T02:08:00.991+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Expression Trees'/><category scheme='http://www.blogger.com/atom/ns#' term='LINQ'/><title type='text'>LINQ dynamic expressions. OrderBy</title><content type='html'>В одном из проектов я писал клиентский компонент который отображал табличные данные (грид). Эти данные приходили из веб сервиса и передавались в грид.&lt;br /&gt;&lt;br /&gt;Веб сервис выглядел так:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;public List&amp;lt;Student&amp;gt; GetStudents()&lt;br /&gt;{&lt;br /&gt;     var query = (from student in db.Students&lt;br /&gt;                  select new&lt;br /&gt;                  {&lt;br /&gt;                       student.StudentID,&lt;br /&gt;                       student.LastName,&lt;br /&gt;                       student.FirstName,&lt;br /&gt;                       ...&lt;br /&gt;                  }&lt;br /&gt;     ...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Потом добавилось требования сортировать грид по нажатию на шапку колонки. Данные о сортируемой колонке должны были передаваться в сервис:&lt;br /&gt;public List&amp;lt;Student&amp;gt; GetStudents(string column, bool asc)&lt;br /&gt;&lt;br /&gt;и тут стал вопрос "а как ?", если у меня анонимный тип. Это потом я уже собираю в Student:&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_8B_3XCoKE6Q/SfdVT5qiocI/AAAAAAAAAE0/596JHt4Ntkk/s1600-h/SS-2009.04.28-22.06.21.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img alt="" border="0" id="BLOGGER_PHOTO_ID_5329822484401398210" src="http://3.bp.blogspot.com/_8B_3XCoKE6Q/SfdVT5qiocI/AAAAAAAAAE0/596JHt4Ntkk/s640/SS-2009.04.28-22.06.21.png" style="display: block; margin: 0px auto 10px; text-align: center;" /&gt;&lt;/a&gt;Вариант в лоб:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;if (!asc)&lt;br /&gt;{&lt;br /&gt;     switch(column)&lt;br /&gt;     {&lt;br /&gt;          case "FirstName": &lt;br /&gt;         query = query.OrderByDescending(p =&amp;gt; p.FirstName);&lt;br /&gt;          break;&lt;br /&gt;    &lt;br /&gt;          case "LastName": &lt;br /&gt;         query = query.OrderByDescending(p =&amp;gt; p.LastName);&lt;br /&gt;          break;&lt;br /&gt;          ...&lt;br /&gt;          ... &lt;br /&gt;    // All columns&lt;br /&gt;     }&lt;br /&gt;}&lt;br /&gt;else&lt;br /&gt;{&lt;br /&gt;     switch(column)&lt;br /&gt;     {&lt;br /&gt;          case "FirstName": &lt;br /&gt;         query = query.OrderBy(p =&amp;gt; p.FirstName);&lt;br /&gt;          break;&lt;br /&gt;&lt;br /&gt;          case "LastName": &lt;br /&gt;         query = query.OrderBy(p =&amp;gt; p.LastName);&lt;br /&gt;          break;&lt;br /&gt;          ...&lt;br /&gt;          ... &lt;br /&gt;    // All columns&lt;br /&gt;     }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Что, не? Та да...  :)&lt;br /&gt;&lt;br /&gt;Второй вариант и наверно единственный (если вы не мазохист) - это использовать LINQ Expressions. Что оно такое и как работает читайте в &lt;a href="http://msdn.microsoft.com/en-us/library/bb397951.aspx"&gt;msdn&lt;/a&gt; или на блогах. Я напишу свое решение относительно моих требований и условий. В результате получится:&lt;br /&gt;&lt;br /&gt;query = query.OrderBy(column, asc);&lt;br /&gt;&lt;br /&gt;Для того чтоб все казалось как "так и надо" я использовал &lt;a href="http://msdn.microsoft.com/en-us/library/bb383977.aspx"&gt;Extension Methods&lt;/a&gt;, которые появились в .NET 3.0.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: csharp; gutter: false;"&gt;public static class OrderByExtension&lt;br /&gt;{&lt;br /&gt;     /// &amp;lt;summary&amp;gt;Orders the sequence by specific column and direction.&amp;lt;/summary&amp;gt;&lt;br /&gt;     /// &amp;lt;param name="query"&amp;gt;The query.&amp;lt;/param&amp;gt;&lt;br /&gt;     /// &amp;lt;param name="sortColumn"&amp;gt;The sort column.&amp;lt;/param&amp;gt;&lt;br /&gt;     /// &amp;lt;param name="ascending"&amp;gt;if set to true [ascending].&amp;lt;/param&amp;gt;&lt;br /&gt;     public static IQueryable&amp;lt;T&amp;gt; OrderBy&amp;lt;T&amp;gt;(this IQueryable&amp;lt;T&amp;gt; query, string sortColumn, string direction)&lt;br /&gt;     {&lt;br /&gt;          string methodName = string.Format("OrderBy{0}", direction.ToLower() == "ascending" ? "" : direction);&lt;br /&gt;&lt;br /&gt;          // .OrderBy{Direction}(p =&amp;gt; p.{ColumnName})&lt;br /&gt;          PropertyInfo property = query.ElementType.GetProperty(sortColumn);&lt;br /&gt;          ParameterExpression parameter = Expression.Parameter(query.ElementType, "p");&lt;br /&gt;          MemberExpression paramAccess = Expression.MakeMemberAccess(parameter, property);&lt;br /&gt;          LambdaExpression orderByLambda = Expression.Lambda(paramAccess, parameter);&lt;br /&gt;&lt;br /&gt;          MethodCallExpression result = Expression.Call(&lt;br /&gt;                    typeof(Queryable),&lt;br /&gt;                    methodName,&lt;br /&gt;                    new[] { query.ElementType, property.PropertyType },&lt;br /&gt;                    query.Expression,&lt;br /&gt;                    Expression.Quote(orderByLambda));&lt;br /&gt;&lt;br /&gt;          return query.Provider.CreateQuery&amp;lt;T&amp;gt;(result);&lt;br /&gt;     }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;В общем виде формирование запроса разбивается на следующие этапы:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Определяем метод сортировки OrderBy или OrderByDescending.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Получить ссылку на поле, которое участвует в результирующей выборке.&lt;/li&gt;&lt;li&gt;Далее формируем lambda выражение: p =&amp;gt; p.[ColumnName], где в [ColumnName] будет подставлено поле, которое мы получили в пункте 2.&lt;/li&gt;&lt;li&gt;Создаем вызов метода OrderBy например, который использует labda из пункта 3.: OrderBy(p =&amp;gt; p.[ColumnName])&lt;/li&gt;&lt;li&gt;Внедряем наш метод в цепочку вызовов: return query.Provider.CreateQuery(result)&lt;/li&gt;&lt;/ol&gt;Пример использования выше. Получилось гламурно :). Если будут вопросы пишите.&lt;br /&gt;&lt;t&gt;&lt;t&gt;&lt;t&gt;&lt;t&gt;&lt;br /&gt;&lt;/t&gt;&lt;/t&gt;&lt;/t&gt;&lt;/t&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-4671038586236281269?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/4671038586236281269/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2009/04/linq-dynamic-expressions-orderby.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/4671038586236281269'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/4671038586236281269'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2009/04/linq-dynamic-expressions-orderby.html' title='LINQ dynamic expressions. OrderBy'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_8B_3XCoKE6Q/SfdVT5qiocI/AAAAAAAAAE0/596JHt4Ntkk/s72-c/SS-2009.04.28-22.06.21.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-3721302783547444499</id><published>2009-04-28T20:57:00.001+03:00</published><updated>2009-11-08T01:53:56.889+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IIS'/><category scheme='http://www.blogger.com/atom/ns#' term='SSRS'/><title type='text'>ReportViewer не работает под IIS 7</title><content type='html'>Недавно пришлось использовать компонент ReportViewer в ASP.NET.&lt;br /&gt;У меня на ноутбуке стоит Vista Business и IIS7.&lt;br /&gt;При открытии страницы в IE, FireFox - получаю кучу JS ошибок. Проблема кроется в новой структуре Web.config файла для IIS 7.&lt;br /&gt;Когда вы добавляете на страницу компонент ReportViewer, студия добавляет ссылку на http handler для этого компонента. Это нужно для того, чтоб ASP.NET использовало сборку Microsoft.Reporting.WebForms.HttpHandler при обращении к ReportViewerWebControl.axd:&lt;br /&gt;&lt;div style="overflow: auto;"&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;&lt;&lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;system.web&lt;/span&gt;&lt;span style="color:blue;"&gt;&gt;&lt;br /&gt;&lt;&lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;httpHandlers&lt;/span&gt;&lt;span style="color:blue;"&gt;&gt;&lt;br /&gt;&lt;&lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;add &lt;/span&gt;&lt;span style="color:red;"&gt;path&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;Reserved.ReportViewerWebControl.axd&lt;/span&gt;"&lt;br /&gt;&lt;span style="color:red;"&gt;verb&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;*&lt;/span&gt;"&lt;br /&gt;&lt;span style="color:red;"&gt;type&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a&lt;/span&gt;"&lt;br /&gt;&lt;span style="color:red;"&gt;validate&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;false&lt;/span&gt;"&lt;span style="color:blue;"&gt;/&gt;&lt;br /&gt;&lt;&lt;!--&lt;/span--&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;httpHandlers&lt;/span&gt;&lt;span style="color:blue;"&gt;&gt;&lt;br /&gt;&lt;&lt;!--&lt;/span--&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;system.web&lt;/span&gt;&lt;span style="color:blue;"&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;В новой структуре секция system.web\httpHandlers теперь перенесена в system.webServer\handlers, но студия редактирует конфиг как для IIS 6.&lt;br /&gt;&lt;br /&gt;Для решения проблемы надо создать соответствующую секцию:&lt;br /&gt;&lt;div style="overflow: auto;"&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;&lt;&lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;system.webServer&lt;/span&gt;&lt;span style="color:blue;"&gt;&gt;&lt;br /&gt;&lt;&lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;handlers&lt;/span&gt;&lt;span style="color:blue;"&gt;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;&lt;span style="color:blue;"&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;&lt;&lt;/span&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;add &lt;/span&gt;&lt;span style="color:red;"&gt;name&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;ReportViewerWebControl&lt;/span&gt;"&lt;br /&gt;&lt;span style="color:red;"&gt;           path&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;Reserved.ReportViewerWebControl.axd&lt;/span&gt;" &lt;span style="color:red;"&gt;&lt;br /&gt;verb&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;*&lt;/span&gt;"&lt;br /&gt;&lt;span style="color:red;"&gt;           type&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;"&lt;span style="color:blue;"&gt;Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a&lt;/span&gt;"&lt;span style="color:blue;"&gt;/&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;   &lt;&lt;/span&gt;&lt;span style="color:blue;"&gt;&lt;!--&lt;/span--&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;handlers&lt;/span&gt;&lt;span style="color:blue;"&gt;&gt;&lt;br /&gt;&lt;!--&lt;/span--&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span style="color:blue;"&gt;&lt;&lt;/span&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;&lt;span style="color:blue;"&gt;&lt;span style="color: rgb(163, 21, 21);"&gt;system.webServer&lt;/span&gt;&lt;span style="color:blue;"&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;сross post: &lt;a href="http://www.blogger.com/Cross%20post:%20http://blogs.msdn.com/vijaysk/archive/2009/01/14/report-viewer-toolbar-does-not-render-properly-on-iis-7-0.aspx"&gt;http://blogs.msdn.com/vijaysk/archive/2009/01/14/report-viewer-toolbar-does-not-render-properly-on-iis-7-0.aspx&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-3721302783547444499?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/3721302783547444499/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2009/04/reportviewer-iis-7.html#comment-form' title='Комментарии: 3'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/3721302783547444499'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/3721302783547444499'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2009/04/reportviewer-iis-7.html' title='ReportViewer не работает под IIS 7'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-329418352356606474.post-6233910958458736071</id><published>2009-03-25T02:53:00.001+02:00</published><updated>2010-03-22T18:03:28.076+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web Performance'/><title type='text'>Улучшение производительности web сайтов. Web Garden</title><content type='html'>Всем привет.&lt;br /&gt;&lt;br /&gt;Сегодня я начинаю свой блог и первую статью я посвящу улучшению производительности web сайтов.&lt;br /&gt;&lt;br /&gt;Что я хочу рассказать:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=329418352356606474&amp;amp;postID=6233910958458736071#t1"&gt;Что такое Web Garden;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=329418352356606474&amp;amp;postID=6233910958458736071#t2"&gt;Как настроить Web Garden на IIS6;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=329418352356606474&amp;amp;postID=6233910958458736071#t3"&gt;Преимущества;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=329418352356606474&amp;amp;postID=6233910958458736071#t4"&gt;Недостатки;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=329418352356606474&amp;amp;postID=6233910958458736071#concl"&gt;Вывод&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;a href="" name="t1"&gt;Что такое Web Garden&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Многие из вас сталкивались с проблемой, когда вашему web приложению трудно справляться с большим количеством подключений. Есть несколько способов решить эту трагедию:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Проапгредить вашу старушку до «Мустанга» - дорого;&lt;/li&gt;&lt;li&gt;Распараллелить web приложение – бесплатно и заманчиво;&lt;/li&gt;&lt;li&gt;Распараллелить чтоб аж Google «завидовал» - в следующем посте (Web Farm).&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Остановимся на 2 решении. Данный механизм называется Web Garden и служит для распараллеливания web приложений используя 1 компьютер. Это решение очень простое и не требует дополнительных ресурсов и растрат.&lt;br /&gt;&lt;br /&gt;Веб садом является web приложение в котором количество рабочих процессов больше 1. Этот механизм появился с IIS 6, в котором также появилась возможность создавать N пулов под N сайтов (max 2000 pools). Каждый пул в IIS 6 по умолчанию работает в режиме isolation mode, это значит, что каждый пул будет иметь свою память, свой контекст и свои границы приложения. Также это значит, что если сайт «упадет» (упаси Билл конечно), то упадет только этот пул, с сайтами которые в нем существуют, но не весь сервер, как было раньше в IIS 5 %). По скольку пул может работать изолированно, мы можем дать ему возможность создавать еще процессы для того, чтоб приложение могло выдерживать больше подключений. Тем самым мы его распараллеливаем, снижая блокировки на ресурсы, используемые приложением. Все запросы, которые приходят в конкретный пул, разделяются между процессами web сада.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;a href="" name="t2"&gt;Как настроить Web Garden на IIS6&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Рассмотрим как это делается.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;В IIS Manager создадим новый пул приложения, в котором будет работать наш сайт, например MyGardenPool.&lt;/li&gt;&lt;li&gt;Затем выбираем в свойствах пула выбираем Properties.&lt;/li&gt;&lt;li&gt;Переходим на вкладку Performance.&lt;/li&gt;&lt;li&gt;В группе Web garden, в поле Maximum number of worker processes устанавливаем количество процессов которое будет выделено вашему пулу. Чтобы приложение работало как web сад, значение должно быть больше 1.&lt;/li&gt;&lt;li&gt;Добавляем web приложение.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Для того чтобы оптимально подобрать количество процессов, поставьте для начала значение, равное количеству ядер или процессоров на вашей машине + 1. А дальше смотрите, если в вашем приложении используются долгие блокирующие запросы к БД, то вам лучше увеличить число выделяющихся процессов. Если запросы короткие и\или не блокирующие, то увеличивать число процессов не надо.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;a href="" name="t3"&gt;Преимущества&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Легко настраивается;&lt;/li&gt;&lt;li&gt;Надежная и устойчивая обработка web сервером запросов. Если один из процессов в пуле будет работать не стабильно или «упадет» по неизвестным причинам, то его сразу подменить другой процесс из пула.&lt;/li&gt;&lt;li&gt;Меньше блокировок на ресурсы. Когда web garden доходит до состоянии «щас лопну», то каждое новое подключение к серверу распределяется между процессами по карусельному принципу.&lt;/li&gt;&lt;li&gt;Рациональное использование N-ядерной или N-процессорной машины;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;a href="" name="t4"&gt;Недостатки&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Если вы используете сессию для хранения каких то данные, то эти данные не будут переданы новому выделенному процессу, т.к. каждый новый процесс - это сам по себе работающий сайт со своей памятью, потоками и т.д. Эту проблему можно решить, например хранить сессию в SQL Server или State Server.&lt;/li&gt;&lt;li&gt;Если у вас выделяется много процессов, то вы можете проиграть в скорости, т.к. время будет уходить на работу деспетчера процессов ОС.&lt;/li&gt;&lt;li&gt;Выделяя больше процессов, вы выделяете под него память которую он «съедает», тем самым уменьшая память под другие возможности приложения, такие как кэш.&lt;/li&gt;&lt;/ol&gt;&lt;span style="font-weight: bold;"&gt;&lt;a href="" name="concl"&gt;&lt;br /&gt;Вывод&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Данный механизм очень хорошо себя оправдывает если вам надо быстро, просто и сердито улучшить производительность и выдерживаемую нагрузку на ваше приложение. Выигрыш в скорости зависит от количества процессоров на вашей машине. Так же не следует забывать о проблемах с сессией и не злоупотреблять количеством выделяемых процессов.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/329418352356606474-6233910958458736071?l=delmadman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://delmadman.blogspot.com/feeds/6233910958458736071/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://delmadman.blogspot.com/2009/03/web-1.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/6233910958458736071'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/329418352356606474/posts/default/6233910958458736071'/><link rel='alternate' type='text/html' href='http://delmadman.blogspot.com/2009/03/web-1.html' title='Улучшение производительности web сайтов. Web Garden'/><author><name>Madman</name><uri>http://www.blogger.com/profile/13588895718633545729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://4.bp.blogspot.com/_8B_3XCoKE6Q/Su3jtEZwuPI/AAAAAAAAAck/QFGyR4po8J0/s1600-R/x_527a6429.jpg'/></author><thr:total>2</thr:total></entry></feed>
