Notas del Terrible
Заметки Ужасного Зануды

FF Addons, part 3

марта 3, 2010 12:10 by terR0Q

В копилку дополнений к FF:


FF 3 User-Agent

марта 3, 2010 11:35 by terR0Q

Firefox 3.6, подпись user-agent:

Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.89 Safari/532.5

Мутант какой-то.


Обновил обзор Umbraco

февраля 12, 2010 15:58 by terR0Q

Дополнил обзор по этому движку парой абзацев (внизу статьи).


Как солить пароли

февраля 11, 2010 10:32 by terR0Q

На заметку ссылка про правильный засол сверток паролей с готовым примером на C# в Martinj's Programming blog: Creating salted hash passwords in C#. С таким уже работал не раз, но вчера пригодилось, когда в очередной раз пришлось писать с нуля.


Когда WCF встречает C/C++

февраля 10, 2010 11:31 by terR0Q

На прошлой неделе 2 дня занимался обертыванием маршалируемой С-шной DLL в лоно сервиса WCF. Занятие кропотливое, особое внимание надо обращаться на #ifdef и #ifndef, когда определяешь обертку типов. Главное внимательно читать документацию про указание маршалируемых типов через атрибут MarshalAs. Надо просто правильно перенести объявления структур в .NET, а далее используя методы класса Marshal перевести все указатели в нужное русло управляемой памяти с нужным типом (правда, не забывая все время явно очищать память).

Плюс к этому общая уже загвоздка с сериализацией контрактных типов от службы: не стоит использовать CollectionDataContract и наследовать передаваемый класс списку, если планируется вводить какие-то свойства, которые также будут передаваться (с атрибутом DataMember) — такие поля будут просто обнуляться после передачи через Proxy. Надо просто объявить обычное свойства типа нужного списка и пометить его, как DataMember, а весь класс — DataContract.

Задача низкоуровневая, и эффективность такова: на 1 метод сервиса пришлось напсиать ~2,000 строк кода.


Obsolete и XmlRoot

февраля 2, 2010 13:38 by terR0Q

Мелочь, а неприятно. Допустим, какой-то класс помечен атрибутом XmlRoot:

[XmlRoot( «SomeXmlNode» )]
public class SomeClass { ... }

Если надо пометить его, как устаревший, атрибут Obsolete надо ставить ДО XmlRoot. Иначе по факту получим еще и XmlIgnore до кучи: при операциях сохранения и чтения XML будут сыпаться ошибки о неизвестном элементе «SomeXmlNode».

Поясню. Вот такой код при сериализации выдаст исключение:

[XmlRoot( «SomeXmlNode» )]
[Obsolete]
public class SomeClass { ... }

А вот такой отработает корректно:

[Obsolete]
[XmlRoot( «SomeXmlNode» )]
public class SomeClass { ... }

Вечером посмотрю, что получается на выходе в IL при таком раскладе. Но ситуация бредовая, при том, что на уровне класса порядок атрибутов роли играть не должен. Скорее всего проблема в реализации XML-сериализатора по умолчанию. Похоже на предположение какого-то архитектора или кодера: «ну раз у нас тут устаревший узел, давайте его проигнорим».

Update.

Чуть позже нашёл в описании класса XmlSerializer заметку:

Objects marked with the Obsolete Attribute no longer serialized. In the .NET Framework 3.5 the XmlSerializer class no longer serializes objects that are marked as [Obsolete].

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


Ещё одно дополнение. При установленном Language Pack SP1 работает обратное поведение: Obsolete должен быть в конце списка атрибутов, чтобы сериализация не ломалась. Как итог, для сериализуемых классов Obsolete лучше вообще не использовать. Такое ощущение, что оригинальный сериализатор делали суровые индусы и они же его потом зачем-то меняли до поставки в Language Pack.


Dictionary и Hashtable

февраля 1, 2010 21:26 by terR0Q

Небольшая резюмирующая заметка про различия между Dictionary и Hashtable в .NET.

Во-первых, словари быстрее для типов значений (т. е. структур: int, float и пр.). В блоге некоего Криса Ньюмана нашел простой и наглядный тест сравнения скорости. HashTable рассчитана на работу с объектами, а не типами значений. Поэтому такие типы будут постоянно упаковываться и распаковываться при работе с таблицей. Если в качестве ключей нужны простые значения, надо использовать словарь.

Ещё одно преимущество словаря — строгая типизация хранимых пар ключ-значение. Это намного удобнее в работе в большинстве случаев.

В случае с объектами всё несколько ровнее. Для эффективной работы хэш-таблицы, необходима полноценная реализация метода GetHashCode, которая для каждого объекта будет создавать уникальный хэш. Уникальность не обязательна, но ускоряет работу. Согласно статье MSDN хэш-таблица сегментирует объекты-значения, ассоциируя их с некоторыми группами кодов. Это может сильно ускорить поиск объекта: будет получен код образца, дальше определён сегмент, а уже в нём будет произведён поиск, а не по всей таблице.

С точки зрения организации внутренних структур словари работают примерно так же, как и хеш-таблицы. Но за счёт использования последовательностей для разрешения конфликтов (короче, когда признак ключа уже есть в таблице) вместо рехеширования, словари обеспечивают поиск с постоянной скоростью — O(1). Хэш-таблица в случае обнаружения существующего хэша, идёт к следующей ячейке до тех пор, пока не найдёт ближайшую свободную. Словарь же просто прикрепляет дубликат в конец цепочки с элементами, попавшими на этот сегмент. При этом в словаре число элементов никогда не превышает число выделенных сегментов. В случае устранения узких мест, это хороший намёк на оценку возможности перейти от словаря к хеш-таблице, но это задача промышленных проектов, где хорошая архитектура чаще решает такую проблему. Зато, что намного актуальнее: если используются объекты и хэш-метод может возвращать неуникальные коды, то использование словарей тем предпочтительнее, чем выше шанс дубликатов. Фактически, если явно функция хеширования не определена, то хэш зависит исключительно от типа объекта (поведение метода в реализации класса Object), и таблица в данном случае будет очень неэффективна.

Последнее замечание, на счет словарей и перечислений. Когда в словарь добавляется очень много записей с Enum в качестве ключа, словарь работает очень медленно. Для ускорения нужно добавить немного кода, который сгладил бы различие с обычным типом-значением, либо подставлять соответствующее int-значение вместо оригинального значения. В развёрнутой статье — “Accelerating Enum-Based Dictionaries with Generic EnumComparer” — всё это подробно объясняется и есть код под. NET 2.0 и 3.5. Если вкратце: по умолчанию для добавления 1 млн. записей требуется 533 мс, что в 10 раз дольше использования int-значения. Если вариант с int по каким-то причинам неудобен, надо реализовать свой Comparer. Правда, у такой ситуации особенность: она очень редкая.

Подробнее вопрос организации внутренних структур коллекций в. NET разобран в отличной статье — “An Extensive Examination of Data Structures Using C# 2.0”.


faster

января 27, 2010 19:06 by terR0Q

Пара внутренних юзеров додумались обрабатывать клиентскую базу на моем сервере (суть — тестовом), потому что он быстрее продуктивного (виртуалка) в n раз. Перекрыл доступ, сменил тестовый логин, выслал мэйл... Может быть, сжалюсь и перекину им базу.


Прекомпиляция в ASP.NET

января 21, 2010 21:14 by terR0Q

В ASP.NET любая страница может состоять из нескольких файлов. Можно ужать и в один, но это неудобно, в Visual Studio все разбивается на 3. Так или иначе, в них содержится программная модель (обработчики разных событий) и серверная разметка (верстка вместе с объявлениями динамических элементов, настраиваемых программно).

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

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

  1. Публикуем сайт в целевой каталог (publish в VS)
  2. В консоли набираем:
    aspnet_compiler. exe -m /LM/W3SVC/1/Root
  3. Ждём завершения и получаем откомпилированный сайт.

Теперь пара моментов. Первое: /LM/W3SVC/1/Root — это адресация сайта в иерархии IIS. Данное значение подходит для всех веб-узлов «по умолчанию», но когда на сервере много узлов, единица заменяется куда более длинным значением. Чтобы узнать точный путь нужна простая утилита — IIS Metabase Explorer. Она покажет всю иерархию узлов, в которой несложно разобраться, т. к. везде присутствуют назначенные читаемые имена узлов. Обнаружив нужный узел, надо взять его параметр MD_APP_ROOT — это и есть тот путь, который должен быть передан aspnet_compiler.

Второе: не стоит запускать компиляцию прямо в рабочем каталоге VS. Если проект активно живёт и в нём участвует сразу несколько человек, скорее всего уже за первый месяц-два там скопится много старых файлов. Если какой-то файл был удалён из проекта у одного разработчика, то у другого при обновлении он будет, скорее всего, просто исключён, а физически останется на диске. А вот когда aspnet_compiler начнёт компиляцию, он будет проходиться по всем файлам в каталоге, найдёт такой мусор и начнёт сыпать ошибки, что соответствующий класс не найден ни в одной сборке, заодно прерывая всю компиляцию (его и нет, ведь VS просто не компилирует необходимого после исключения из проекта).

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


Spart.Aaa(aaa).Find(aa=>aa.AAA.Equals("AAA"));

декабря 24, 2009 13:12 by terR0Q

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

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

Первое правило, если вместо мега-крутости и ковбоистости хочется развить результативность и качество: используй много простых правил для достижения сложных результатов. Так и жить легче, и точность выше. Хотя есть еще другая закономерность: свое место находят как заносчивые и умелые раздолбаи, так и аккуратисты. Первые последних часто обвиняют в низком интеллекте (обоснование — легкое привыкание к рутине); вторые делятся на неопытных или просто плохо обучаемых (и часто рефлексивных на счет первых), и тех, кто спокойно делает свое дело и пишет в любом стиле. Но я искренне надеюсь, что мне не придется писать в спартанском стиле, тем более на своих проектах могу отыграться.


Many many screens

декабря 21, 2009 18:43 by terR0Q

[lots of screens] on desktop

Покорило. До кучи ссылка на очень полезную программу, RealTimeSoft's UltraMon, из блога Coding Horror.


XML в SQL Server

декабря 8, 2009 12:26 by terR0Q

Довелось поработать с XML в SQL Server 2005, впечатления отличные.

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

Теперь про технические подробности. Если XML-поле не будет часто использоваться, то можно просто добавить в таблицу столбец типа XML и особо дальше на заморачиваться. Хотя при этом не все возможности XQuery в запросах будут применимы: часто требуется указание точного типа данных, что делается именно схемой. А если поле будет выбираться почти постоянно, надо не только его индексировать (create primary xml index on), но и создать на сервере схему, к которой привязать столбец.

Разница в скорости работы запросов получается, грубо говоря, трехкратная. Без схемы и индекса один мой тест показывал 30 мс, со схемой и индексом - 8 мс. Без схемы, но с индексом было порядка 16 мс. При значительном росте данных, в 30 раз, скорость обработки уменьшилась только в 10 раз, но я проводил тесты при порядке чисел в десятки тысяч, но не миллионов. Была возможность сравнить скорость с аналогичной логикой работы для обычных столбцов, и разница была в районе 30% - это цена динамики и возможности использовать XQuery в запросах.

Для создания схемы используется простая операция добавления схемы с глобальным именем в рамках базы (схемы попадают в Programmability \ Types \ XML Schema Collections):

create xml schema collection some_Usable_Schema as
'<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="YetAnotherList">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" name="Item">
          <xs:complexType>
            <xs:attribute name="Type" type="xs:int" use="required" />
            <xs:attribute name="Count" type="xs:int" use="required" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>';

Далее при создании столбца после указания типа (XML) в скобках добавляем ссылку на схему:

ColumnName xml (Content имя_схемы)

Подытоживая, могу сказать: использовать очень просто, возможности XML в БД дает огромные, но применение должно быть оправдано, т.к. усложняется разработка и возможно ухудшение производительности (а это обычно надо компенсировать).


12 Steps to Better Code

декабря 4, 2009 11:20 by terR0Q

Уже не раз упоминал 12 пунктов определения хорошей программной конторы от Джоэла Спольски, но все недосуг было полную статью прочитать в оригинале. Восполняю, тем паче статье уже 9 лет.


Программирование для детей

декабря 1, 2009 00:35 by terR0Q

Помог Ниноке на простом примере разобраться с базовыми понятиями .NET Remoting :)

terR0Q (12:09): ну смари. вот есть твое приложение «ежик». оно хочет задать вопрос приложению «слоник». вопрос у ежика прост — сколько времени? т.к. ежик и слоник говорят на разных языках, ежик пишет вопрос слонику на бересте (ну вот знает он язык слоника) и дает слонику. слоник читает, пишет ответ и отдает обратно. это вариант передачи объекта в контекст — произведенный слоником объект был передан обратно ежику (в его контекст). а пример другого случая — когда они умеют говорить на одном языке, и информацию передают вербально. слова произносятся и никому не передаются — не происходит передачи объекта для передачи информации. понятно стало?

Ниноко (12:10): в рамках ежиков и слоников да : ))

terR0Q (12:12): как вариант, в данном случае мы могли бы указать табличке, написанной ежиком, оставаться у ежика, и тогда слонику пришлось бы читать, глядя на табличку с рук ежика

Ниноко (12:14): а что с точки зрения слоников у ежиков значит «экземпляры класса не могут создаваться в контексте, в котором установлена синхронизация»?

terR0Q (12:15): это значит, что пока слоник читает с рук ежика, ежик не может написать еще одну табличку


Worst working conditions

ноября 20, 2009 10:20 by terR0Q

Хорошему вопросу хороший ответ:

Interruptions. Those are the mind killer. Programming requires concentration and juggling facts and threads together as you carefully braid them into logic.

Hot weather sucks. Old computers suck. Buggy compilers suck. Stupid bosses and ignorant clients suck. Demanding schedules suck.

But none of those is a killer like constant interruptions. If you can’t concentrate, nothing will get done even under otherwise ideal situations.


 
MapYourVisitors.COM