пятница, 2 октября 2009 г.

LINQ. First vs. Single

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

Для получения первого элемента, удовлетворяющего предикату, в LINQ используются два основных метода: First и Single. В различных реализациях LINQ детали могут меняться, поэтому для определенности рассмотрим логику этих методов на примере LINQ To Objects: First просто возвращает первый элемент (а если его нет - генерирует исключение), а Single возвращает единственный элемент (а исключение генерирует не только если элементов нет, но и если их больше одного). Однако несмотря на то, что названия методов вполне отражают различия между ними, порой программисты относятся к этим двум методам как к равноценным и взаимозаменяемым.

Давайте немного углубимся в алгоритмы работы методов: First находит первый элемент - и возвращает его, Single же на этом не останавливается и продолжает поиск второго элемента, дабы либо убедиться, что его нет, либо сгенерировать исключение, если он все же есть. Как следствие, First всегда быстрее Single. Таким образом, для получения первого элемента всегда нужно использовать First, Single же пригодится лишь в тех случаях, когда необходимо быть уверенным, что полученное значение является единственным (как бы банально это ни звучало, порой об этом забывают).

Вооружимся Reflector'ом и от теории перейдем к практике.

Для начала убедимся, что методы First и Single работают именно по описанным выше алгоритмам. Для этого рассмотрим выдержки кода метода First:

foreach (TSource local in source)
{
if (predicate(local))
{
return local;
}
}
throw Error.NoMatch();

и Single:

TSource local = default(TSource);
long num = 0L;
foreach (TSource local2 in source)
{
if (predicate(local2))
{
local = local2;
num += 1L;
}
}
long num2 = num;
if ((num2 <= 1L) && (num2 >= 0L))
{
switch (((int) num2))
{
case 0:
throw Error.NoMatch();

case 1:
return local;
}
}
throw Error.MoreThanOneMatch();

Если с методом First все ясно, то к реализации метода Single у меня есть вопросы. В ходе цикла num не проверяется на равенство двум, поэтому выполнение цикла в любом случае продолжается до конца коллекции. Сразу представляется параноидальная ситуация, когда Single'ом с "тяжелым" предикатом обрабатывается огромная коллекция с объектами с "тяжелой" перегрузкой Equals, и все ужасно тормозит... Если в комментариях не последует оперативного разъяснения (может, я чего проглядел, а может, это известная "фича"), обращусь за оным в MSDN'овские форумы.

В заключение скажу пару слов об Entity Framework, а точнее о LINQ To Entities.

В EF v. 1 LINQ To Entities не поддерживал метод Single. Особенно неожиданным это открытие становилось для тех, кто переходил с, казалось бы, менее функционального LINQ To SQL: EF оказался заложником своей универсальности (по сравнению с заточенным под одну СУБД LINQ To SQL). Хотя на практике горевать по этому поводу приходилось редко (особенно на фоне более "выдающихся" ограничений EF v. 1), потому что, как правило, такие запросы выполняются по первичному ключу, уникальность которого гарантируется на уровне БД. Сейчас нет под рукой VS 2010... но как доберусь до нее - проверю, реализовали ли эту возможность (соответствующие планы у команды EF были) в текущем public-build'е EF4 и обновлю пост.

Update. Продолжение в этом посте.

среда, 23 сентября 2009 г.

Пустите меня в Интернет!

Так вышло, что последние несколько дней я провел в Китае (всего несколько часов как на Родине). При заселении в гостиницу на ресепшине сказали, что Интернета у них нет, чем меня очень удивили. Поднимаюсь в номер на 12-й этаж - ничего подобного: стабильно ловится 3-4 беспроводных соединения (причем два из них незащищенные). Защищенные соединения я брать во внимание не стал: взламывать WEP/WPA не было ни времени, ни желания. Да и черт знает, может, у них за эту безобидную шалость смертная казнь полагается :)

Одно незащищенное соединение сразу привлекло мое внимание, ибо называлось оно TP-LINK. TP-LINK - производитель дешевого (по сравнению с Cisco) сетевого оборудования с соответствующим качеством (по крайней Wi-Fi адаптером их производства, "счастливым" обладателем которого я одно время являлся, я остался недоволен - пришлось вернуть по гарантии). Я это к тому, что была высокая вероятность, что это именно домашняя сеть с расшаренным по незнанию (а, может, и по доброте душевной) соединением. Так и вышло. Правда интернет был не ахти (вечером вообще казалось, что через эту вайфайку сидит полкитая), да и сигнал был слабеньким и периодически пропадал. Это заставило меня взглянуть на второе соединение.

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

Я уж было собрался идти за сим-картой... но тут мне на глаза попалась надпись, гордо провозглашавшая количество пользователей (точную цифру не помню, но счет был на десятки тысяч). Тут я понял, что теория вероятности сегодня на моей стороне. Глаза загорелись - уже и забыл, что собирался за симкой. Итак, выбираем первый способ подключения (схема авторизации там немного необычная - через ActiveX-компонент, но это не суть важно) и пытаемся подобрать пароль (помня огромное число абонентов). Так... Какие самые популярные логины и пароли? (задача усложняется тем, что нужно подобрать не только пароль, как это обычно бывает, а пару логин-пароль). Пробую:
1. Логин: qwerty Пароль: qwerty - мимо
2. Логин: qwery Пароль: 12345 - Successfully connected
Со второго раза - вот так брутфорс :)

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

Мораль: Не используйте стандартные логины и пароли. Пусть уж лучше они будут несложные - главное, чтобы свои, родные, с намеком на уникальность.

P. S. Хотел написать этот пост еще в Китае (как полагается, со скринами)... но меня упорно не пускали ни на blogger.com, ни на blogspot.com (ни на их поддомены). Видимо, это проделки Великого Китайского Фаервола. Попытался воспользоваться анонимайзером - не пускает. Наверное, нужно было найти свеженькую анонимную проксю... но я поленился.

PP. S. Китайцы - красавцы: трудолюбивые, чертяги.

вторник, 15 сентября 2009 г.

Правим баги. Подсознательно

Попросил меня сегодня коллега помочь разобраться с багом. Ничто не предвещало серьезных проблем (хотя чужой код - всегда потемки). Однако наскоком решить проблему не удалось. По прошествии двух часов, я уже перепробовал все мыслимые-немыслимые варианты, нагуглил целый ворох подобных топиков/постов - все не то. Попробовал отвлечься ненадолго - результат тот же. Сидел уже в каком-то отрешенном состоянии (думаю, каждый программист бывал в подобной ситуации, когда от кажущейся безысходности руки опускаются и голова отказывается искать новые варианты). И тут неожиданно в голову пришла мысль: "А не убрать ли мне этот using (оставив, конечно же, сам код внутри блока)" (речь об операторе using в C#, который после выхода из блока вызывает метод Dispose). Мысль возникла изниоткуда: я не пришел к ней логическим путем - просто неожиданно мне захотелось убрать using и посмотреть, что будет. Все еще находясь в "анабиозе", я запустил приложение - к моему удивлениею, все заработало. После этого разобраться, в чем же была истинная причина ошибки, было делом техники.

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

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

PP. S. С какой легкостью после этого закрывается море вкладок в браузере, которые, как казалось, могли помочь...

вторник, 4 августа 2009 г.

Перевод книги "Semantic Web Programming"

Около полугода назад я пытался добиться перевода книги "SEMANTIC WEB for the WORKING ONTOLOGIST" (об этом я писал здесь и здесь). Насколько мне известно, процесс заглох на этапе переговоров между издательствами.

На этот раз сложилась обратная ситуация: к семантическому сообществу с инициативой публикации перевода одной из книг по тематике Semantic Web обратился Виктор Штонда, генеральный директор издательства "Диалектика". В блоге Виктора Вы можете найти пост с обсуждением данного вопроса. Так что если Вас интересует перевод, - не стесняйтесь, присоединяйтесь к обсуждению :)

P. S. Название поста обусловлено тем, что на данный момент наиболее вероятен перевод именно книги "Semantic Web Programming" (о которой я, кстати, недавно писал).