Sunday, December 14, 2008

Artorius: абстрактному синтаксическому дереву(AST) быть в NHibernate.

В репозиторий неофициальных дополнений к NHibernate одним из его ведущих разработчиков Fabio Maulo был добавлен проект Artorius. Проект призван решить давнюю проблему - отсутствие  абстрактного синтаксического дерева внутреннего языка запросов. В текущей версии обе основных технологии запросов HQL и Criteria API используют свои уникальные методы трансляции в SQL. Предполагается реализация внутреннего nHQL-AST, именно в который и будут преобразовываться запросы созданные с помощью доступных технологий: HQL( имеются ввиду строковые запросы), Criteria API, LINQ или любого другого языка запросов(кроме нативного SQL).

На текущий момент в репозитории есть определенние грамматики HQL в форме Бэкуса — Наура и реализация ast, при создании ast из строки hql используется модифицированный парсер GOLD:

image

Все тесты проекта на данный момент  направлены на тестирование грамматики, но как видно по коду разработка самого дерева тоже уже ушла  достаточно далеко, что позволяет сделать вывод: ast в NHibernate быть.

Tuesday, December 2, 2008

Visual Studio Team System 2008 Database Edition GDR - RTM

Visual Studio Team System 2008 Database Edition GDR - RTM - очень полезная новость, для тех кто разрабатывает для SQL Server в этой среде(я отношусь к этим людям). Поскольку это практически новый релиз инструмента, в нем очень много новых(недостающих) возможностей. Всем кто с ним работает советую "обновить" (установить) версию.

Feature list

  • Project System
    • Full support for SQL Server 2000, SQL Server 2005 and SQL Server 2008 support.
      • All database objects are now modeled, therefore they are compared and part of deployment. The side-effect of this is that there are no more objects that need to be stored in pre- and post-deployment scripts.
    • Server vs. Database projects
      • This change allows you to model the different behavior of shared and server level objects inside SQL Server vs. user database level objects. 
    • Partial projects
      • Partial project allow you to share implementation between projects, with a single definition of the code
    • Composite projects
      • Composite projects extend database references, allowing you to add objects from other project to the name database schema namespace. So you can compose your database from other projects and/or DBSCHEMA file references
    • Database references using literals, in addition to SQLCMD token support
      • The addition of literal support facilitates that you do not have to change your code of 3-part names, when do do not require the flexibility of name independent deployment
    • Reference support for XML Schema Collections to include .XSD files
      • Enables single sourcing of XML Schema Collections from an .XSD file which itself can be used by other developers or tools.
    • Reference support of SQL-CLR projects and / or SQL-CLR assemblies
      • Enables the inclusion of a SQL-CLR project or a binary, to represent a SQL-CLR assembly. Like XSD references, the goal is single sourcing of artifacts inside the development environment
    • Import Schema and Import Script is now available through DTE
      • This allows the creation of macros or programmatic add-in to drive these common tasks.
    • No more "DesignDB"
      • The change to a fully model driven implementation makes the need for a local SQL Server database instance obsolete. Having a sandbox for your development environment is still am advised practice though, so you can test the changes independently and in an isolated environment.
  • Build
    • Build validates the consistency of the schema model and produces all the files that you need to deploy the database
  • Deploy
    • Deploy takes the output from the build stage and deploys this information based on the settings supplied
    • Deployment settings are now isolated in to the .SQLDEPLOYMENT file, which allows you to standardize and re-use the deployment settings
    • Most of the "Schema Compare" comparison options are now available as the deployment options, to increase the symmetry between the two
    • VSDBCMD.EXE, is a command line deployment tool, which enables the standalone deployment of Database Project
  • Schema Compare:
    • Compares any combination of live databases, Database Projects and .DBSCHEMA files
    • Session level option support, which can be persisted and re-used
    • Object type level filtering allow you to exclude object types from the comparison
    • You can now quickly navigate back and forward between difference via the toolbar and optionally configurable keyboard shortcuts
    • Substitution of SQLCMD variables, allows for the comparison of projects that rely on the use of SQLCMD variables
    • Schema Compare can now be executed through DTE
  • Data Compare
  • Refactoring:
    • New refactoring types: Expand Wildcards and Fully-qualify Names (in addition to Rename, Move Schema and 3/4 part name refactoring)
    • The patented "preservation of intend" functionality makes is possible that the Deployment Engine will deploy renames and move schema operations as actual renames and move schema operations, instead of a DROP / ADD operation.
    • Extensibility enables the creation of your own refactoring types (operations) and refactoring targets.
  • Database Unit Testing
    • Added support for SQL Server 2008
  • Data Generation
    • BulkCopy based inserts in addition to the existing INSERT data sync
    • Sequential Databound Generator is now included in the product
  • T-SQL Static Code Analysis
    • Execute analysis using MS Build and/or as part of the VS IDE build process
    • Including the ability to write you own T-SQL Static Code Analysis rules
  • Extensibility
    • Public full fidelity .NET based T-SQL parser for SQL Server 2000, 2005 and 2008.
    • Public .NET based SQL Script DOM, for programmatically generation of SQL code (equivalent to the Code DOM in .NET)
    • Access to the Schema Model from within Visual Studio, this enables for example ability to write code generators based on the schema model.
    • Feature extensibility:
      • T-SQL Static Code Analysis rules
      • Refactoring types, these are the refactoring operations
      • Refactoring targets, these are the sources that you want to update as part of a refactoring operation
      • Data Generators
      • Data Distributions
      • Test Conditions

Monday, December 1, 2008

Ресурс на тему паттернов, антипаттернов и рефакторинга.

SourceMaking - ресурс, посвященный аспектам разработки программного обеспечения, перечисленным в названии темы сообщения. Таких ресурсов немало в сети, но на этом информация хорошо структурирована и красиво оформлена.

Улыбнули изображения категорий антипаттернов:

 

 

                    Антипаттерны разработки

 

 

 

 

 

 

                    Антипаттерны архитектуры

 

 

 

 

 

 

                    Антипаттерны управления проектом

 

 

 

Не могу не согласиться с Дмитрием Лобасевым: "Ребята здорово постарались!".

Wednesday, November 26, 2008

Autumn of Agile Screencast Series.

Летом Stephen A. Bohlen (вчера, кстати, он стал членом команды NHibernate) записал серию скинкастов "Summer of NHibernate", подробно демонстрирующую основные возможности NHibernate. Так же в ней демонстируются способы эффективного тестирования с помощью TestDriven.NET и MbUnit  и способы организации тестов для кода доступа к данным с использованием NDbUnit. Я думаю данная серия будет интересна многим разработчикам, даже не работающим с NHibernate.

Autumn

В сентябре же Stephen начал новую серию - "Autumn of Agile". В этой серии он рассматривает основные agile-принципы разработки изнутри (со стороны процесса разработки). Мне кажется получится очень достойная серия, советую смотреть.

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

Saturday, November 22, 2008

Породы программистов(часть 2).

В сообщении Породы программистов(часть 1) были перечислены основные породы программистов, здесь же будут перечислены редкие.

Волшебник

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

Минималист

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

Аналогист

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

Трюкач

Трюкачи слишком увлекаются разными технологическими трюками.Они постоянно осваивают разные новинки, но результат от этого не улучшается. По правде говоря, нас всех в той или иной степени привлекают забавные технологические приемы. Я вот, например, помню мой первый компьютер. Он был аналоговым, и, аоворачивая диски, я переключал ветви в предустановленном аппаратном алгоритме. Эта штука была похожа на гипертрафированную логарифмическую линейку. В общем, я до сих пор люблю забавляться со всякими высокотехнологическими штуковинами.Если вам приходится работать с трюкачами, попытайтесь направить их увлечение игрушками на решение их первоочередной задачи, а именно на производство бизнес-решения. Если им удалось втиснуть на экран, который, как предполагается, будет работать с разрешением 800х600, 30 разных элементов пользовательского интерфейса, это еще совершенно не обозначает, что они решили сво задачу в соответствии с реальными потребностями пользователей. Трюкачи, при всех их познаниях в технологии, часто не могут усвоить конечное назначение программы. Полагая, сто их функции ограничиваются забавами с разными интрументальными средствами, они отказываются учитывать те аспекты программирования, благодаря которым мы не затрачиваем на сопровождение титанических усилий.

Разгильдяй

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

Тормоз

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

Любитель

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

Профан

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

Эклектик

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

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

Благодарности автору книги "Herding Cats. A primer of programmers who lead programmers" J. Hank Rainwater'у за хороший материал и отдельное спасибо моей супруге Ирине за помощь, в переносе материала.

Thursday, November 20, 2008

Породы программистов(часть 1).

Недавно была опубликована интересная заметка, которая заставила меня вспомнить о книге "Herding Cats. A primer of programmers who lead programmers" by J. Hank Rainwater. В ней, по-моему мнению, очень точно описаны типичные породы программистов :

Основные:

  • архитектор;
  • конструктивист;
  • художник;
  • инженер;
  • ученый;
  • лихач.

Редкие:

  • волшебник;
  • минималист;
  • аналогист;
  • трюкач;
  • разгильдяй;
  • тормоз;
  • любитель;
  • профан;
  • эклектик.

Архитектор*

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

* - Термином архитектор в данном случае обозначен один из личностных типов программистов и совершенно не имеется в виду полноценный программный архитектор.

Конструктивист

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

Художник

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

Инженер

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

Ученый

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

Лихач

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

Описание редких пород читайте в последующих записях блога.

Wednesday, November 19, 2008

Интересный факт о безопасности загрузки сборок из GAC.

Поскольку я по образованию "защитник информации"(075400 МИЭТ), то постоянно интересуюсь новостями в этой области, и сегодня увидел интересную статью по теме.  Автор описывает способ внедрения вредоносного кода в разные части .NET Framework, например, в метод аутентификации из System.Web.dll с последующей пересылкой логинов и паролей злоумышленнику, в метод чтения строк подключения к базе данных, компрометации классов из System.Security.Cryptography,  javascript кода XSS в System.Web.dll, компроментации CAS т.е. отключение  CodeAccessPermission::Demand(),   CodeAccessPermission::Deny(),   CodeAccessPermission::Assert(),  FileIOPermission, RegistryPermission, и т.п., простого backdoor  в любой код Framework'а, ну и прочих гадостей, не подчиняюшихся CAS. Им так же написана утилита позволяющая частично автоматизировать этот процесс.  Конечно это "post exploitation" атака, т.е. что бы все это проделать необходимы права администратора на машине, но это на самом деле не так важно.

Изменение кода управляемой библиотеки представляет собой тривиальную задачу(даже подписанной), но основная польза от исследования автора заключается в том, что он отметил следующее поведение: если сборка загружена в GAC, то загрузчик не пытается проверять подпись, а просто загружает сборку по соотвествующему(версия, культура, маркер открытого ключа) физическому пути(например для mscorlib.dll C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll). Именно этот факт, позволяет просто перекомпилировать сборки из GAC, игнорируя хаки с подписью совсем, с дальнейшим копированием сборок по соответствующему физическому пути файловой системы. Кстати, стоит отметить, что для сборок(подписанных, разговор только о них), которые не установлены в GAC, проверка осуществляется каждый раз при загрузке приложения.

Monday, November 10, 2008

Байт-код провайдеры в NHibernate 2.1

Один из авторов NHibernate Fabio Maulo сообщил в своем блоге, что на данный момент в trunk внесены изменения позволяющие выбирать между несколькими прокси-генераторами. До сих пор использовался Casle.DynamicProxy2, теперь его монополия нарушена LinFu, который по словам автора(и по словам Fabio, кстати, тоже) быстрее предыдущего, планируется, так же, возможность подключения фабрики прокси из Spring.NET.

В конфигурацию session-factory добавлен новый обязательный параметр - proxyfactory.factory_class:

<hibernate-configuration  xmlns="urn:nhibernate-configuration-2.2" >
<session-factory name="YourAppName">
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<property name="connection.connection_string">
Server=(local);initial catalog=nhibernate;Integrated Security=SSPI
</property>
<property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>
</session-factory>
</hibernate-configuration>



Для тех кто использует в своих приложения IOC контейнер Windsor или реализацию Active Record для NHibernate от Castle, тем благоразумнее остаться с DynamicProxy2, остальным же можно выбирать. Возможность заменять такую важную деталь(как генерация прокси) очень важна для проекта NHibernate, и еще раз подтверждает его расширяемость. Поскриптум Fabio ))): "Let me say that something strange happened in my heart when I had remove the last reference to Castle in NH-Core and NH-Tests."

Friday, November 7, 2008

IntelliJ IDEA 8 Released.

idea8_header

Сегодня был выпущен релиз одной из самых лучших(возможно самой лучшей) IDE для разработки на java от компании JetBrains. Далее приводится информация с официального сайта:

Languages & Frameworks

Features & Improvements

От себя хочу добавить одну мысль, которая давно сформировалась в голове, в процессе долгой работы с продуктами от компании JetBrains: "Все сделано просто и работает максимально эффективно, т.е. специально для людей, а не для киборгов!". Спасибо. Побольше бы таких продуктов.

Monday, November 3, 2008

Путешествия из .NET в Java и обратно с IKVM.NET

И так, сегодня хочу упомянуть  проект IKVM.NET - реализация Java для .NET Framework и Mono с открытым исходным кодом. IKVM.NET состоит из: 

  • виртуальной машины(JVM) реализованной на .NET;
  • реализации на .NET библиотеки основных классов из OpenJDK;
  • набора средств обеспечивающих возможность взаимодействия .NET и Java.

Поскольку в описании для IKVM.AWT.WinForms.dll сказано:

Very limited and broken implementation of a few AWT peers. This is a low priority issue until the platform stabilizes and works sufficiently well.

решил осуществить путешествие с использования именно "слабой" стороны(предпочитаю именно такой подход). Сделал маленькое приложение на Java:

java -jar ikvm-test.jar

image

Запустил на ikvm(на машине не установлена java):

ikvm -jar ikvm-test.jar

image

Улыбнуло. Выполнил:

ikvmc ikvm-test.jar

на выходе ikvm-test.exe, запуск:

image 

Снова улыбнуло. Далее вызвал команду:

ikvmstub mscorlib.dll

на выходе получил mscorlib.jar ))), на который можно ссылаться при компиляции приложений на java, однако запуск таких приложений возможен только с использованием ikvm или в .net/mono после использования ikvmc.

Конечно, вряд ли стоит говорить о полном воссоединении технологий, но сам по себе проект очень занимательный, которому, несмотря на то, что он еще находится в разработке, уже нашлось применение в реальной жизни: Eclipse, JmDNS, JGroups, Jetty. Вообще, у автора IKVM.NET очень интересный блог, особенно для тех, кто глубоко интересуется  обоими технологиями.

Sunday, October 26, 2008

Миграция кода из Visual Source Safe в Subversion.

В компании, в которой я на данный момент работаю, для хранения исходного кода используется Subversion (и TFS), но есть старые проекты, которые живут в Microsoft Visual Source Safe 2005. Было p_svnimporterпринято решение перенести старые проекты (код и историю) в Subversion. Первым инструментом, который я нашел, оказался Importer for SVN, написан на java, по описанию умеет экспортировать из: CVS, PVCS, VSS, ClearCase, MKS, StarTeam.

К сожалению, экспорт поддерживается только из VSS 6.0, поэтому сразу успешно импортировать с его помощью мне не удалось. На форуме разработчиков был обнаружен патч, решающий мою проблему. Забрав с сервера последнюю ревизию кода приложения и применив патч, я собрал версию, которая позволила мне импортировать код и историю в Subversion из Microsoft Visual Source Safe 2005. Ура! 

Патч изменяет два класса из пакета org.polarion.svnimporter.vssprovider.internal(VssUtil.java и Vss.java). Для тех, кому понадобится производить подобную миграцию,  я выложил в сеть получившийся билд, убрав лишние провайдеры для других систем контроля версий.

Thursday, October 16, 2008

Object/Object Mapping(отображение объектов на объекты) в .NET

object-to-object mappingИспользуя в своих приложениях доменную модель (Domain Model), как способ организации бизнес логики, в сервисах приложения сталкиваешься с постоянной необходимостью преобразования объектов домена в объекты данных (Data Transfer Object - DTO) для передачи их во внешние системы, различным клиентским приложениям и т.п.  К сожалению, для .NET я смог найти  только следующие библиотеки:

  1. Otis-lib ;
  2. NBearMapping;
  3. Business Logic Toolkit (проект на rsdn);
  4. NPersist;

Со второй не получилось даже толком познакомится, т.к. ни документации, ни примеров я не обнаружил (но как я понял из исходников, она не дотягивает до следующих двух).  Последние две направлены в большей мере на решение O/R M задач, и поддерживают O/O M как подмножество функций необходимых для решения основных задач (объектно-реляционная проекция), поэтому отсутствие необходимых, для решения моей задачи, функций и наличие в них лишних функций (мой O/R M меня устраивает :-)) вынудили меня вычеркнуть их из списка. Таким образом осталось только одна библиотека - Otis.

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

<?xml version="1.0" encoding="utf-8" ?>
<otis-mapping xmlns="urn:otis-mapping-1.0">

<class name="Otis.Sample.Presentation.ArtistInfo, Otis.Sample" source="Otis.Sample.Domain.Artist, Otis.Sample" >
<member name="Id" />
<member name="Description" expression="[$Name + ' [' + $Country + ']' ]" />
<member name="Records" />
<member name="RecordCount" expression="$Records.Count" />
</class>

<class name="Otis.Sample.Presentation.RecordInfo, Otis.Sample" source="Otis.Sample.Domain.Record, Otis.Sample" helper="ConversionHelper" >
<member name="Id" />
<member name="Description" expression="[$Name + ' [' + $YearPublished + ']' ]" />
<member name="SongCount" expression="$Songs.Count" />
<member name="TotalCommentCount" expression="count:$Songs/Comments" />
<member name="AverageSongRating" expression="avg:$Songs/Ratings/Value" />
<member name="AverageSongDuration" expression="avg:$Songs/DurationInSeconds" format="{0:N2} seconds" />
<member name="AlbumDuration" expression="sum:$Songs/DurationInSeconds" />
<member name="Category" expression="$Style" >
<map from="['Pop']" to="Pop"/>
<map from="['Punk']" to="Rock"/>
<map from="['Rock']" to="Rock"/>
<map from="['Classical']" to="Classical"/>
</member>
</class>

</otis-mapping>


Критические недостатки реализации Otis-lib:
  • невозможность использования в серверных приложениях (отсутствует поддержка многопоточности, кэша сгенерированных классов, сборок);


  • использование CodeDom для генерации кода преобразователей;


  • нерасширяемая архитектура;


Ко всем недостаткам реализации добавляются низкие темпы развития проекта. Со всеми этими недостатками Otis-lib становится непригодной в использовании на реальных проектах.

Мои поиски продолжаются, может быть Вы знаете еще о каких-нибудь преобразователях (object to object mapper)?

Friday, October 10, 2008

Самый быстрый способ создания объектов в .NET 3.5

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

public class MyType
{
public MyType(){}

public MyType(string s){}

public MyType(int i, string s){}

public MyType(int i){}

public MyType(object obj, string s){}
}

Вызова натурального конструктора.

var obj= new MyType(10000);

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

Вызов метода  CreateInstance у класса Activator.

var obj = (MyType)Activator.CreateInstance(typeof(MyType), 10000);

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

Вызов метода Invoke у объекта класса ConstructorInfo.

//предварительные затраты, результат должен кэшироваться
ConstructorInfo constructorInfo = typeof(MyType).GetConstructor(new[] { typeof(int) });
//вызов конструктора
var obj = (MyType)constructorInfo.Invoke(new object[] { 10000 });

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

Вызов делегата на динамический метод.

//предварительные затраты, результат должен кэшироваться
var dm = new DynamicMethod("MyCtor", typeof(MyType), new[] { typeof(int) }, typeof(MyType).Module, true);
ILGenerator ilgen = dm.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Newobj, type.GetConstructor(new[] { typeof(int) }));
ilgen.Emit(OpCodes.Ret);
var ctor = (Func<int, MyType>)dm.CreateDelegate(typeof(Func<int, MyType>));
//вызов конструктора
var obj = ctor(10000);

В этом случае с помощью класса DynamicMethod мы динамически создаем статический метод "MyCtor" у нашего класса MyType, единственной задачей которого является создание экземпляра класса MyType через нужный конструктор. Далее мы получаем, делегат на сгенерированный метод и можем его вызывать. Этот метод достаточно быстр: не велики предварительные затраты и время вызова приблизительно в 2-3 раза больше, чем время вызова натурального конструктора, что на много быстрее, в сравнении с ранее рассмотренными способами. Минусом способа является его сложность, немногие разработчики смогут понять этот код.

Вызов динамически скомпилированного лямбда-выражения.

//предварительные затраты, результат должен кэшироваться
var param = Expression.Parameter(typeof(int), "param");
var constructorCall = Expression.New(typeof(MyType).GetConstructor(new[] { typeof(int) }), param);
var constructorLambda = Expression.Lambda(constructorCall, param);
var ctor = (Func<int, MyType>)constructorLambdaTest.Compile();
//вызов конструктора
var obj = ctor(10000);

Основной идеей способа является динамическое создание лямбды вида:

param => new MyType(param)

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

Какую роль играет время затрачиваемое на подготовку конструктора?

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

// время генерации + время вызова
var ctorForT1 = ObjectFactory.Create<int, TContract>(typeof(T1));
var objOfT1 = ctorForT1(100);
// время генерации + время вызова
var ctor2ForT1 = ObjectFactory.Create<int, string, TContract>(typeof(T1));
objOfT1 = ctor2ForT1(100,"test");
// время генерации + время вызова
var ctorForT2 = ObjectFactory.Create<int, string, TContract>(typeof(T2));
var objOfT2 = ctorForT2(100,"test");

Этот случай далек от реального, но имеет место быть, поэтому в этом случае лучше выбрать 4-ый способ (динамический метод). Если же вероятность повторного создания экземпляра класса через уже использованный конструктор высока(обычно именно так и бывает):

// время генерации + время вызова
var ctorForT1 = ObjectFactory.Create<int, string, TContract>(typeof(T1));
var objOfT1 = ctorForT1(100,"test");

for (int i = 0; i < 10000; i++)
{
// время изъятия конструктора из кэша(пренебрежительно мало и одинаково для всех способов) + время вызова
var obj = ObjectFactory.Create<int, string, TContract>(typeof(T1))(i, "test" + i)
}

то следует выбрать 5-й способ (лямбда-выражение).
Существует еще несколько способов создания объектов в .NET. Я привел не все способы создания объектов в этой статье намеренно, выбрав лишь, по-моему мнению, самые подходящие для использования в фабрике объектов.

Результаты измерений.

Конфигурация:

Intel Core™ Duo Processor T2000 Sequence
2048 Mb of RAM
Windows XP SP3 .NET 3.5 SP1

Время на предварительные затраты(10000 итераций) - Время на создание экземпляра класса(1000000 итераций).

Конструктор с параметрами

Вызова натурального конструктора
00:00:00 - 00:00:00.0242500

Вызов метода CreateInstance у класса Activator
00:00:00 - 00:00:06.5156250

Вызов метода Invoke у объекта класса ConstructorInfo
00:00:00.0156250 - 00:00:02.3593750

Вызов делегата на динамический метод
00:00:00.3281250 - 00:00:00.0615500

Вызов динамически скомпилированного лямбда-выражения
00:00:02.7968750 - 00:00:00.0312500

Конструктор без параметров

Вызова натурального конструктора
00:00:00 - 00:00:00.0222500

Вызов метода CreateInstance у класса Activator
00:00:00 - 00:00:00.2812500

Вызов метода Invoke у объекта класса ConstructorInfo
00:00:00.0076250 - 00:00:01.7812500

Вызов делегата на динамический метод
00:00:00.2968750 - 00:00:00.0625000

Вызов динамически скомпилированного лямбда-выражения
00:00:02.5468750 - 00:00:00.0312500

Код тестового примера:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;

namespace ObjectFactoryTest2
{

public class MyType
{
public MyType() { }

public MyType(string s) { }

public MyType(int i, string s) { }

public MyType(int i) { }

public MyType(object obj, string s) { }
}

public class ObjectFactoryTest
{
delegate void Approach(out DateTime prepareStart, out DateTime executeStart, out DateTime finish);
static int LOOP=1;
static int PREPARE_LOOP=1;
static readonly Type type = typeof(MyType);

public static void Main(string[] args)
{
const int TEST_LOOP = 10;
LOOP = 1000000;
PREPARE_LOOP = 10000;

for (int i = 0; i < TEST_LOOP; i++)
{
Console.WriteLine();
Console.WriteLine("--------Ctor with Parameters #" + (i+1) + "--------");
Console.WriteLine();
Console.WriteLine("ParameterAndCtor");
Measure(ParameterAndCtor);
Console.WriteLine();
Console.WriteLine("ParameterAndActivator");
Measure(ParameterAndActivator);
Console.WriteLine();
Console.WriteLine("ParameterAndCtorInfo");
Measure(ParameterAndCtorInfo);
Console.WriteLine();
Console.WriteLine("ParameterAndDynamicMethodAndDelegate");
Measure(ParameterAndDynamicMethod);
Console.WriteLine();
Console.WriteLine("ParameterAndExpressionAndDelegate");
Measure(ParameterAndExpressionAndDelegate);
}

Console.ReadLine();

for (int i = 0; i < TEST_LOOP; i++)
{
Console.WriteLine();
Console.WriteLine("--------Parameterless Ctor #" + (i+1) + "--------");
Console.WriteLine();
Console.WriteLine("ParameterlessAndCtor");
Measure(ParameterlessAndCtor);
Console.WriteLine();
Console.WriteLine("ParameterlessAndActivator");
Measure(ParameterlessAndActivator);
Console.WriteLine();
Console.WriteLine("ParameterlessAndCtorInfo");
Measure(ParameterlessAndCtorInfo);
Console.WriteLine();
Console.WriteLine("ParameterlessAndDynamicMethod");
Measure(ParameterlessAndDynamicMethod);
Console.WriteLine();
Console.WriteLine("ParameterlessAndExpressionAndDelegate");
Measure(ParameterlessAndExpressionAndDelegate);
}

Console.ReadLine();

}

static void Measure(Approach approach)
{
DateTime prepareStart, executeStart, finish;
approach(out prepareStart, out executeStart, out finish);
Console.WriteLine("{0} - {1}", executeStart - prepareStart, finish - executeStart);
GC.Collect(2);
GC.WaitForPendingFinalizers();
}

private static void ParameterAndCtor(out DateTime prepareStart, out DateTime executeStart, out DateTime finish)
{
prepareStart = DateTime.Now;
executeStart = prepareStart;
for (int i = 0; i < LOOP; i++)
new MyType(i);
finish = DateTime.Now;
}

private static void ParameterAndActivator(out DateTime prepareStart, out DateTime executeStart, out DateTime finish)
{
prepareStart = DateTime.Now;
executeStart = prepareStart;
for (int i = 0; i < LOOP; i++)
Activator.CreateInstance(type, i);
finish = DateTime.Now;
}

private static void ParameterAndCtorInfo(out DateTime prepareStart, out DateTime executeStart, out DateTime finish)
{
ConstructorInfo ci = null;
prepareStart = DateTime.Now;
for (int i = 0; i < PREPARE_LOOP; i++)
ci = type.GetConstructor(new[] { typeof(int) });

executeStart = DateTime.Now;
for (int i = 0; i < LOOP; i++)
ci.Invoke(new object[] { i });
finish = DateTime.Now;
}

private static void ParameterAndDynamicMethod(out DateTime prepareStart, out DateTime executeStart, out DateTime finish)
{
Func<int, MyType> dlTest = null;
prepareStart = DateTime.Now;
for (int i = 0; i < PREPARE_LOOP; i++)
{
var dmTest = new DynamicMethod("MyCtor", type, new[] { typeof(int) }, type.Module, true);
ILGenerator ilgenTest = dmTest.GetILGenerator();
ilgenTest.Emit(OpCodes.Ldarg_0);
ilgenTest.Emit(OpCodes.Newobj, type.GetConstructor(new[] { typeof(int) }));
ilgenTest.Emit(OpCodes.Ret);
dlTest = (Func<int, MyType>)dmTest.CreateDelegate(typeof(Func<int, MyType>));
}
executeStart = DateTime.Now;
for (int i = 0; i < LOOP; i++)
dlTest(i);
finish = DateTime.Now;
}

private static void ParameterAndExpressionAndDelegate(out DateTime prepareStart, out DateTime executeStart, out DateTime finish)
{
Func<int, MyType> dlTest = null;
prepareStart = DateTime.Now;
var paramTest = Expression.Parameter(typeof(Int32), "n");
for (int i = 0; i < PREPARE_LOOP; i++)
{
var constructorCallTest = Expression.New(type.GetConstructor(new[] { typeof(int) }), paramTest);
var constructorLambdaTest = Expression.Lambda(constructorCallTest, paramTest);
dlTest = (Func<int, MyType>)constructorLambdaTest.Compile();
}
executeStart = DateTime.Now;
for (int i = 0; i < LOOP; i++)
dlTest(i);
finish = DateTime.Now;
}

private static void ParameterlessAndCtor(out DateTime prepareStart, out DateTime executeStart, out DateTime finish)
{
prepareStart = DateTime.Now;
executeStart = prepareStart;
for (int i = 0; i < LOOP; i++)
new MyType();
finish = DateTime.Now;
}

private static void ParameterlessAndActivator(out DateTime prepareStart, out DateTime executeStart, out DateTime finish)
{
prepareStart = DateTime.Now;
executeStart = prepareStart;
for (int i = 0; i < LOOP; i++)
Activator.CreateInstance(type);
finish = DateTime.Now;
}

private static void ParameterlessAndCtorInfo(out DateTime prepareStart, out DateTime executeStart, out DateTime finish)
{
ConstructorInfo ci = null;
prepareStart = DateTime.Now;
for (int i = 0; i < PREPARE_LOOP; i++)
ci = type.GetConstructor(Type.EmptyTypes);

executeStart = DateTime.Now;
for (int i = 0; i < LOOP; i++)
ci.Invoke(null);
finish = DateTime.Now;
}

private static void ParameterlessAndDynamicMethod(out DateTime prepareStart, out DateTime executeStart, out DateTime finish)
{
Func<MyType> dlTest = null;
prepareStart = DateTime.Now;
for (int i = 0; i < PREPARE_LOOP; i++)
{
var dmTest = new DynamicMethod("MyCtor", type, Type.EmptyTypes, type.Module, true);
ILGenerator ilgenTest = dmTest.GetILGenerator();
ilgenTest.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes));
ilgenTest.Emit(OpCodes.Ret);
dlTest = (Func<MyType>)dmTest.CreateDelegate(typeof(Func<MyType>));
}
executeStart = DateTime.Now;
for (int i = 0; i < LOOP; i++)
dlTest();
finish = DateTime.Now;
}

private static void ParameterlessAndExpressionAndDelegate(out DateTime prepareStart, out DateTime executeStart, out DateTime finish)
{
Func<MyType> dlTest = null;
prepareStart = DateTime.Now;
for (int i = 0; i < PREPARE_LOOP; i++)
{
var constructorCallTest = Expression.New(type.GetConstructor(Type.EmptyTypes));
var constructorLambdaTest = Expression.Lambda(constructorCallTest);
dlTest = (Func<MyType>)constructorLambdaTest.Compile();
}
executeStart = DateTime.Now;
for (int i = 0; i < LOOP; i++)
dlTest();
finish = DateTime.Now;
}

}
}









Friday, September 12, 2008

NHibernate 2.o и Linq.

Недавно вышел релиз NHibernate 2.0 (сейчас ожидается sp1). К сожалению, в данной версии отсутствует официальная поддержка Linq. На данный момент существует экспериментальная реализация Linq for NHibernate. Инициирована она была одним из разработчиков NHibernate Oren Eini и находилась в его репозитории, потом была перенесена в репозиторий NHibernate Contrib. Реализация основана на NHibernate 2.0 Beta2 (Daniel Guenter реализовал патч, позволяющий работать ей не с бета-версий, а с релизом), имеет достаточное количество недоработок и дефектов, поэтому использовать ее в реальных проектах пока не рекомендуется. Допустим, этот простой запрос не будет работать:

[Category("JOIN")]
[Test]
[Description("This sample uses foreign key navigation in the " +
"from clause to select all orders for customers. Not actual.")]
public void DLinqJoin1c()
{
var q =
from c in db.Customers
from o in c.Orders.Cast<Order>()
where o.OrderID=100
select c;

var list = q.ToList();

ObjectDumper.Write(q);
}

Так же, в этих же тестах очень много [Ignore("TODO")].

Полную же поддержку Linq for NHibernate, разработчики обещают в NHibernate 2.1. Сейчас разработка Linq for NHibernate перенесена в репозиторий(trunk) NHibernate, поскольку полная реализация требует незначительных изменений последнего. Из моей переписки с одиним из разработчиков Linq for NHibernate я выяснил, что теперь избран путь генерации SQL при обработке выражений(expression tree), вместо использования Criteria API(как это делается в экспериментальной реализации). Будем следить за изменениями и ждать выхода версии 2.1.

Thursday, September 11, 2008

Сервисы в Domain-Driven дизайне.

В предметной области (домене) встречаются операции, которые не являются частью сущностей (Entity) или простых объектов-значений (Value Objects). Некоторые из них, по сути, представляют некоторое действие или деятельность, но следуя парадигме Domain Driven Design (DDD), где все является объектами, эти действия необходимо представить как объекты. Распространенной ошибкой является вынесение логики (поведения) из сущностей и объектов-значений, и помещение ее в классы сервисов. Сложные операции, в итоге, переполняют объект, искажая его первоначальную роль, поэтому операция в сервисе должна объединять и координировать логику из других доменных объектов, связывая объекты таким образом, что бы представляемые ими концепции, могли рассматриваться отдельно. В отличие от сущностей и объектов-значений сервисы объявлены в терминах того, что они могут сделать для клиента. Имена сервисов строятся на основании деятельности, которую они представляют, в отличие от сущностей.

Основными характеристиками хорошего сервиса являются:

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

Примеры сервисов домена: IOrderProcessor, IProductFinder, IFundsTransferService.

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

  • cервисы приложения(Application);
  • сервисы предметной область(Domain);
  • инфраструктурные сервисы(Infrastructure).

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

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

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


Application:
Funds Transfer App Service
- обрабатывает входные данные (например запрос в формате XML);
- обращается к сервису домена для осуществления перевода;
- ждет подтверждения;
- решает посылать ли нотификацию через инфраструктурный сервис.

Domain:
Funds Transfer Domain Service
- взаимодействует с необходимыми объектами Account и Ledger, вызывая соответствующую логику предметной области(домена);
- предоставляет результат (разрешен ли перевод или нет и т.п.).

Infrastructure:
Send Notification Service
- посылает e-mail'ы, факсы и осуществляет другую нотификацию поддерживаемую приложением.

Литература: "Domain-Driven Design: Tackling Complexity in the Heart of Software" by Eric Evans.

Monday, September 8, 2008

Почему я не использовал модульное тестирование 3 года назад.

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

  1. Я был в заблуждении относительно выгоды использования модульного тестирования. Модульное тестирование повышает качество вашего приложения, что на самом деле в дальнейшем так же упрощает модифицируемость и поддержу приложения. В будущем, если продвинуться на следующую ступень и освоить Test Driven Development (разработку основанную на тестировании:)), модульное тестирование становится неотъемлемой частью процесса формирования дизайна (детальной архитектуры) вашего приложения. "TDD это не тестирование, потому что вы не думаете как тестер, когда занимаетесь им, вы думаете как дизайнер." - Scott Bellware.
  2. Как и многие другие, я привык думать, что тестирование это проблема QA инженеров, но, ни как не моя. Я не знаю, откуда была такая уверенность, но сейчас я убежден, что это отговорка неквалифицированных разработчиков. Тестирование - это процесс нахождения дефектов (багов) в приложении, но это так же, в равной степени, и процесс проверки написанного (кода) на то, что оно работает как и задумывалось. Может быть разработчику и сложно найти дефект в написанном им приложении, но кто лучше него может убедится в том, что написанный им код, делает именно то, что он хотел?
  3. Тестирование это не особо весело. Сидеть перед монитором, вбивать входные данные и проверять, что все работает как и задумывалось, полный "ацтой". Но написание модульных тестов это тоже программирование, которое ничем не отличается от работы выполняемой нами каждый день. Временами, как и в разработке других частей приложения, это не совсем гениальный код, но при его написании совершенствуются имеющиеся навыки работы и осваиваются новые подходы и техники.
  4. Написание тестов занимает время. На самом деле модульное тестирование не тратит ваше время, оно экономит его. Время, затрачиваемое на проверку после внесения изменений и на исправление дефектов намного превышает время написания модульных тестов. Если честно, написание и модификация тестов действительно отнимает время (особенно много, когда вы только начинаете использовать модульное тестирование). У вас может не хватать времени на написание модульных тестов или ваш клиент(руководство) могут не понимать, что эти временные затраты оправданы, в таких случаях необходимо выделить критически важные секции кода вашего приложения, и производить, по возможности, модульное тестирование на них.

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

Литература: "Foundations of Programming: Building Better Software" by Karl Seguin.

Wednesday, April 16, 2008

Отладка Windows Service в Visual Studio.

Многие проекты в качестве host-среды используют стандартные службы Windows. Но к сожлению, не все разработчики знакомы с эффективными методами отладки служб, поэтому вновь и вновь, открывая решения своих коллег, я вижу там или дублирующее консольное-host приложение или "голый" сервис, который нужно сначала инсталлировать, запустить, а потом присоединяться отладчиком к его процессу. Существует же несколько способов отладки служб и достачное количество информации в сети, описывающей эти способы. Я хочу выделить, как мне кажется, самый краткий и эффективный способ, с которым, можно нажать F5 и спокойно ждать, когда произойдет попадание в нужную точку останова(если конечно в текущей конфигурации сборки определена константа DEBUG).

using System;
using System.ServiceProcess;
using System.Threading;

namespace DebugWindowsService
{
partial class TestService : ServiceBase
{
public TestService()
{
InitializeComponent();
}

protected override void OnStart(string[] args)
{
// TODO: Add code here to start your service.
}

protected override void OnStop()
{
// TODO: Add code here to perform any tear-down necessary to stop your service.
}

static void Main(string[] args)
{
TestService service = new TestService();
#if (!DEBUG)
ServiceBase.Run(service);
#else
service.OnStart(args);
Console.WriteLine("Debugging session was started. OnStop and other windows service specific methods will not be called.");
Thread.Sleep(Timeout.Infinite);
#endif
}
}
}