Web application configuration management for different target environments

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

Содержание:

  1. Классический способ “в лоб” управления конфигурацией.
  2. Где хранить конфигурационные настройки веб приложения для разных окружений?
  3. Лирическое отступление на тему хороших манер при работе с конфигурационными файлами.
  4. Как использовать ASP.NET web.config transformations для генерации конфигурационных файлов для разных окружений (dev, staging, production).
  5. Использование ASP.NET web.config transformations для любого XML файла, а не только для web.config.
  6. Как используя ASP.NET web.config transformations, иметь возможность компилировать приложение на машине без установленного Visual Studio и ASP.NET, а лишь с .NET Framework 4.0.
  7. Пример использования ASP.NET web.config transformation синтаксиса.
  8. Автоматический выбор конфигурационного файла для целевого окружения, и подготовка deployment package посредством собственного скрипта, написанного на Python.

Tech talk in Grid Dynamics on unit testing, TDD, BDD

Сегодня выступал с докладом в харьковском офисе компании Grid Dynamics на тему How to build better software with unit testing, TDD, BDD. Иначе говоря, как эти методологии помогают создавать ПО и делать его лучше. Пару слов о содержании презентации и самооценке результатов.

Содержание презентации

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

Презентации, созданные в prezi.com - построены на использовании технологии Flash. И в целом такие презентации выглядят поживее и подинамичней, чем их PowerPoint собратья. Можно создавать неплохие анимационные эффекты, которые могут удачно подчеркнуть идею, например делая zoom in/zoom out при переходе между master/detail слайдами. Главное, не переборщить с поворотами, особенно, если слайды меняются довольно часто - может закружится голова. Короче говоря, лучше один раз увидеть, чем что раз услышать.

Изначально, идея была рассказать о "Behavior driven development", но по мере создания презентации фокус сместился в сторону unit-тестов и test-driven development.

  1. Важность методик unit-testing, TDD, BDD. Их цель.
  2. Unit testing: why, how, what's in practice?
  3. Понятие test-first development. Шаги к его пониманию.
  4. Test driven development. TDD as a software development process. Как использование TDD улучшает дизайн приложения, как помогает при рефакторинге, как помогает фокусироваться на поведение и другие вопросы?
  5. Пример написания кода в BDD-стиле из проекта MemcacheIt с применением BDD-framework SimpleSpec.

Результаты

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

  1. Сократить время выступления до 20 минут против порядка 40.
  2. Убрать раздел о unit-тестах. Все и так знают, что это такое. Сосредоточиться на TDD и BDD.
  3. Сократить объем теории и показывать больше примеров.
  4. Возможно, показать живой пример создания тестов, написания кода модуля, и запуска тестов в IDE, нежели показывать принт-скрины кода на слайдах.

Как бы там ни было, спасибо ребятам из Grid Dynamics, которые проявили гостепреимство и посвятили почти час своего времени моему докладу. Надеюсь, было интересно.

SimpleSpec - yet another BDD framework

Сегодня продолжим разговор о Behavior-Driven Development - рассмотрим использование BDD-фреймворка SimpleSpec для описания спецификаций. Статья предполагает у читателя наличие базовых знаний в области Behavior-driven development. В одном из моих прошлых постов описана идея BDD, его отличие от модульного тестирования, существующие фреймворки, такие как MSpec.

Содержание:

  1. Что такое SimpleSpec?
  2. Ключевые идеи и цели
  3. Знакомство с Simple.Spec в scenario-behavior стиле
  4. Behavior-scenario стиль
  5. Behavior-suite стиль
  6. Scenario validation
  7. Сокрытие реализации спецификаций
  8. Выводы

Что такое SimpleSpec?

SimpleSpec is a simple BDD framework for .NET of xSpec type. Specifications are written at a unit level.

SimpleSpec относится к xSpec типу фреймворков, использует given-when-then как язык для описания спецификаций на уровне модулей (unit-level). На данный момент, это тонкая обертка над NUnit, которая используя возможности этого unit-testing фреймворка добавляет семантику BDD, позволяя разработчику описывать спецификации в стиле BDD. Не требует конфигурации, нестандартных unit-test раннеров, внешних зависимостей, инструментов - только NUnit и SimpleSpec. Проект размещен на github.

Да, это еще один BDD-фреймворк, автором которого являюсь Я.
- "Очередной велосипед!?" - скажете Вы?
- Скорее результат моего личного опыта работы с MSpec, осмысление его недостатоков и поиска более простого и лучшего BDD решения.

Ключевые идеи и цели

Простота

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

Сделать так, чтобы реализация BDD в виде фреймворка, не отпугивала разработчиков от самой идеи BDD, как это я часто наблюдаю, когда показываю смесь из статических классов, анонимных делегатов, конструкций =()=> из MSpec. После такой презентации говорить о высоких идеях behavior-driven development уже бесполезно. Фреймворк должен воплощать BDD идеи и быть простым.

Читабельность и документирующая функция

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

Подобная идея разделения не нова, фреймворки типа xBehave (NBehave) так и делают - описание спецификации вынесено в отдельный файл (plain text + custom Domain specific language), а реализация спецификация в другом файле (unit test with C#), и постоянная синхронизация в нагрузку. Такой подход годится для описания спецификаций высокоуровневых пользовательских историй (user stories) в виде приемочных критериев (acceptance criteria), когда обязанности описания и реализации спецификаций разделены между разными ролями (QA, Developer). Однако использование его на уровне модулей (unit-level) привело бы к излишнему усложнению - нужно было бы решать вопросы поиска, синхронизации, реализации собственных DSL.

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

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

Поддержка разных стилей описания поведения

Context-specification - это общепринятый стиль описания спецификаций, при котором описывается набор сценариев, каждый из которых содержит набор поведений, которые в наблюдаются в том или ином сценарии. На мой взгляд, сontext-specification - несколько неудачное название, которое не отражает сути. Я предпочитаю называть такой стиль - Scenario-behavior.

Однако не всегда удобно описывать спецификации в модели "cценарий - наблюдаемые поведения". SimpleSpec поддерживает также модель, при которой описывается поведение, которое наблюдается в тех или иных сценариях (Behavior-scenario), либо просто описывается набор поведений без явного выделения сценария (Behavior suite).

Знакомство с Simple.Spec в scenario-behavior стиле

Познакомимся с Simple.Spec на примере спецификации поведения класса, который анализирует посещаемость сайта, в частности для текущего дня позволяет узнать как изменилась посещаемость сайта по отношению к предыдущему дню.

Спецификация с ключевыми моментами выглядит так:

1. This is scenario specification.
Спецификация описана в стиле scenario-behavior (context-specification), то есть двигаемся от сценария к наблюдаемым поведениям. Для этого помечаем класс атрибутом Scenario.Spec.

2. Give a short name to the scenario.
Имя класса используем в качестве краткого описания сценария: when_calculating_visit_count_variation.

3. Use base class for common stuff.
В базовой класс with_site_attendance_analyzer выносим общие вещи, которые повторяются от сценария к сценарию: вспомогательные методы, создание нужных объектов. Однако надо соблюдать баланс между желанием вынести все общее за скобки и самодостаточностью спецификации. В идеале, спецификация не должна быть перегружена повторяющимися деталями и реализацией, но в тоже время для понимания спецификации должно быть достаточно взглянуть на один класс спецификации, без необходимости перемещаться между классами. Некоторые разработчики очень любят строить глубокую иерархию, однако я бы рекомендовал использование максимум одного уровня иерархии. Большее количество уровней излишне усложнит поддержку спецификаций.

4. Give a detailed description of the scenario in constructor.
Подробно описываем сценарий в конструкторе. Описание сценария состоит из задания контекста (given) и вызова действия (when).

5. Setup context using givens.
Используем одну или больше конструкций Given(...) для задания контекста.

6. Custom Domain specific language using methods and delegates.
Обычно задание контекста - это именно то, что чаще всего повторяется от сценария к сценарию. Здесь имеет смысл смоделироть логику создания контекста в виде методов, переместить их в базовый класс и вызывать их с нужными параметрами. attendance_statistics_analyzer(...) - создает объект SiteAttendanceAnalyzer, собственно это то, что мы тестируем, и его зависимости. attendance_statistics(...) - задает данные о статистике посещения сайта. Подобный подход позволяет нам убить трех зайцев: выделить повторяющиеся вещи в базовый класс и разгрузить спецификации, разделить описание спецификации и ее реализацию, создать собственный Domain specific language для описания конкретного сценария.

7. Specify action using when.
Используем контрукцию When(...) для описания действия. Сценарий может содержать только одну конструкцию When. Если посмотреть на код, то здесь мы используем анонимный делегат. Было бы излишним усложнением для одной строчки кода создавать отдельный метод. Более того, вызов метода analyzer.CalculateVisitVariation(...) и так говорит сам за себя.

8. Specify one or more observable behaviors.
После того как сценарий описан, описываем наблюдаемые поведения. Для того чтобы пометить поведение, используется атрибут Behavior. Несколько поведений в сценарии - это вполне валидная ситуация, более того в context-specification стиле это даже поощряется, так что не нужно ограничивать себя одним поведением на сценарий. В данном стиле cценарий - это то, что является первичным, и то, от чего следует отталкиваться.

9. Do not use NUnit assertions. To be completely BDD'y use shoulds.
Так или иначе, спецификация должна содержать фазу проверки. Вполне можно использовать механизмы Assert.That, которые есть в NUnit, но я бы рекомендовал использовать любую библиотеку для проверок в BDD стиле, которая предлагает конструкции типа Should(...) (Fluent.Assertions, NUnit.Should, и др.).

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

  • Описание спецификации отделено от реализации. Все детали реализации и тестирования вынесены в методы.
  • Для структурирования спецификаций используется язык given-when-then, а также применяется собственный DSL, засчет именования методов.
  • Ориентированность на поведение, а не на API-модуля. Так или иначе, вызовы API модуля никуда не делись, но они являются деталями реализации и не интересуют нас в первую очередь, а поэтому скрыты.

Behavior-scenario стиль

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

[Behavior.Spec]
public class attendance_data_should_not_pass_validation : with_site_attendance_analyzer
{
    public attendance_data_should_not_pass_validation()
    {
        Given(attendance_statistics_analyzer);
        CouldFailWith<ValidationException>().Verify(ShouldFail);
    }

    [Scenario]
    public void when_visit_variation_threshold_is_exceeded()
    {
        Given(() => attendance_statistics(
            new AttendanceSummary(new DateTime(2011, 7, 1), 10, TimeSpan.FromMinutes(1), 2, 13),
            new AttendanceSummary(new DateTime(2011, 7, 2), 15, TimeSpan.FromMinutes(2), 3, 15),
            new AttendanceSummary(new DateTime(2011, 7, 3), 16, TimeSpan.FromMinutes(3), 4, 16)));
        Given(() => analyzer.LoadClientStatistics(resource, new DateTime(2011, 1, 1), new DateTime(2012, 1, 1)));
        When(() => analyzer.Validate(0.4));
    }

    [Scenario]
    public void when_statistics_is_not_contiguous_with_some_days_missing()
    {
        Given(() => attendance_statistics(
            new AttendanceSummary(new DateTime(2011, 7, 1), 10, TimeSpan.FromMinutes(1), 2, 13),
            new AttendanceSummary(new DateTime(2011, 7, 3), 11, TimeSpan.FromMinutes(2), 3, 15),
            new AttendanceSummary(new DateTime(2011, 7, 4), 12, TimeSpan.FromMinutes(3), 4, 16)));
        Given(() => analyzer.LoadClientStatistics(resource, new DateTime(2011, 1, 1), new DateTime(2012, 1, 1)));
        When(() => analyzer.Validate(0.4));
    }
}

Подход behavior-scenario противоположен подходу scenario-behavior. Имя класса отражает поведение. Класс помечен атрибутом Behavior.Spec. В конструкторе задается контекст, общий для всех сценариев, хотя в данном случае это необязательно и сделано, чтобы разгрузить последующее описание сценариев от повторяющихся элементов, которые не добавляют смысла. Ключевой момент - это описание поведения один раз в конструкторе. Далее описываются сценарии, в которых наблюдается данное поведение, соотвествующие методы помечаются атрибутом Scenario. Сценарий, как и прежде, состоит из задания контекста (given) и выполнения действия (when).

Вот как выглядит результаты выполнения спецификации:

Стиль behavior-specification стоит использовать только тогда, когда есть фиксированный малый набор поведений, которые проявляются в большом количестве сценариев. Иначе, стоит придерживаться scenario-behavior стиля.

Behavior-suite стиль

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

[Behavior.Spec]
public class device_collection_behaviors
    : specification
{
    private DeviceCollection Devices;
    private Device A1Device = new Device(1, "A1", "A", "X", null);
    private Device A1DuplicateDevice = new Device(1, "A1", "A", "Z", null);
    private Device A2Device = new Device(1, "A2", "A", "Y", null);
    private Device B1Device = new Device(1, "B1", "B", "X", null);

    public device_collection_behaviors()
    {
        Given(() => Devices = new DeviceCollection(new[]
        {
            A1Device,
            A1DuplicateDevice,
            A2Device, 
            B1Device
        }));
    }

    [Behavior]
    public void should_get_devices_by_name()
    {
        Devices.GetByName("A1").Should().BeEquivalentTo(A1Device, A1DuplicateDevice);
        Devices.GetByName("B1").Should().BeEquivalentTo(B1Device);
        Devices.GetByName("C").Should().BeEmpty();
    }

    [Behavior]
    public void should_get_devices_by_type()
    {
        Devices.GetByType("A").Should().BeEquivalentTo(A1Device, A1DuplicateDevice, A2Device);
        Devices.GetByName("B").Should().BeEquivalentTo(B1Device);
        Devices.GetByName("C").Should().BeEmpty();
    }

    [Behavior]
    public void should_get_devices_by_supplier_name()
    {
        Devices.GetBySupplierName("X").Should().BeEquivalentTo(A1Device, B1Device);
        Devices.GetBySupplierName("Y").Should().BeEquivalentTo(A2Device);
        Devices.GetBySupplierName("C").Should().BeEmpty();
    }
}
В примере выше описывается поведение класса-коллекции устройств - DeviceCollection. В конструкторе задан общий контекст. В методах, помеченных атрибутом Behavior, совмещен вызов API модуля и проверка результатов. По внешнему виду, этот подход больше напоминает классические юнит тесты.

Scenario validation

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

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

[Scenario.Spec]
public class when_calculating_visit_count_variation_for_a_day_with_no_attendance_data 
    : with_site_attendance_analyzer
{
    public when_calculating_visit_count_variation_for_a_day_with_no_attendance_data()
    {
        Given(attendance_statistics_analyzer);
        Given(() => attendance_statistics(
            new AttendanceSummary(new DateTime(2011, 6, 1), 4, TimeSpan.FromMinutes(1), 2, 12),
            new AttendanceSummary(new DateTime(2011, 6, 3), 8, TimeSpan.FromMinutes(2), 10, 123),
            new AttendanceSummary(new DateTime(2011, 6, 4), 5, TimeSpan.FromSeconds(50), 4, 6)));
        Given(() => analyzer.LoadClientStatistics(resource, new DateTime(2011, 6, 1), new DateTime(2011, 6, 3)));
        When(() => analyzer.CalculateVisitVariation(new DateTime(2011, 6, 2)));
        CouldFailWith<ArgumentException>();
    }

Подобно примеру ранее, в конструкторе класса описан сценарий. Обратите внимание на строчку.

        CouldFailWith<ArgumentException>();
Мы не проверяем, что сценарий корректен или некорректен, мы лишь декларируем, что сценарий может быть некорректен.

А далее два пути: либо проверка на корректность, либо на некорректность.

    [Behavior]
    public void should_fail()
    {
        Then(() => ShouldFail("does not have attendance statistiscs"));
    }
    [Behavior]
    public void should_not_fail()
    {
        Then(() => ShouldNotFail());
    }

Сокрытие реализации спецификаций

Еще один элемент паззла - это наш базовый класс, в который мы выносим те или иные вспомогательные конструкции.

public class with_site_attendance_analyzer : specification
{
    public SiteAttendanceAnalyzer analyzer;
    public Mock<IAttendanceStatisticsProvider> billingDataProvider;
    public Resource resource;

    protected void attendance_statistics_analyzer()
    {
        resource = new Resource { Uri = new Uri("http://bdd.com/simplespect") };
        billingDataProvider = new Mock<IAttendanceStatisticsProvider>();
        analyzer = new SiteAttendanceAnalyzer(billingDataProvider.Object);
    }

    protected void attendance_statistics(params AttendanceSummary[] attendances)
    {
        billingDataProvider
            .Setup(provider => provider.FetchStatistics(resource, It.IsAny<DateTime>(), It.IsAny<DateTime>()))
            .Returns(attendances);
    }

    protected void attendance_statistics(
        DateTime startDate, 
        DateTime endDate, 
        params AttendanceSummary[] attendances)
    {
        billingDataProvider
            .Setup(provider => provider.FetchStatistics(resource, startDate, endDate))
            .Returns(attendances);
    }
}
Можно сказать, что стоит так или иначе скрывать все то, что не является описанием спецификации. Сокрытие осущетвляется путем выделения методов.
  • Код, который повторяется от сценария к сценарию, и не привносит смысловой нагрузки.
  • Детали реализации спецификации, низкоуровневая работа с API модуля.
  • Код, который представляет собой механизмы тестирования, к примеру, создание и конфигурация фейков.
Снова же, основная цель - разделение описания и реализации спецификации. Выделение базового класса - лишь механизм предоставления доступа разным сценариям к одним и тем же элементам. Если у вас всего два сценария, то скорее всего выделение базового класса неоправдано.

Выводы

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

Интересно также рассматривать идею Behavior-driven development в связке с Domain-driven design (DDD). DDD - позволяет коду модудей говорить на языке предметной области, BDD - позволяет коду тестов/cпецификаций говорить на языке предметной области. Идея BDD удачно гармонирует c DDD, но в тоже время вполне можно использовать BDD без применения DDD.

SNAP - Keeping aspects in a container

Hi, this is my first blog post in English, as I want to share it with an English-speaking audience too. Today I want to speak about aspect oriented framework named SNAP, build simple logging aspect, then describe why it is bad to let SNAP manage aspects instances on its own, and how it restricts us as a developers in building complex aspects. Then I show how I've solved the problem described.

Aspect Oriented Programming with SNAP

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

В экосистеме .net cуществует много средств, задача которых предоставлять механизмы логгирования и трассировки. Это могут быть сторонние библиотеки как log4net, NLog. Также остается недооцененным функционал трассировки и логгирования, поставляемый в .net из коробки (NET Framework System.Diagnostics). Есть решения, которые расширяют стандартные средства трассировки .net. В Microsoft Enterprise Library для этих целей есть Logging Application Block, также есть third-party библиотеки, например Essential Diagnostics.

Недостатка в средствах трассировки и логгирования нет. В целом, идея одна и та же, предоставляемый функционал варьируется в ту или иную сторону. Однако, сегодня речь не о выборе logging framework. Более важный вопрос, на мой взгляд, не в выборе той или иной библиотеки, а в ее использовании в нашем приложении, другими словами, как происходят вызовы API библиотеки из нашего кода.

Содержание статьи.

  1. Cross-cutting concerns
  2. Aspect orientation
  3. Классификация AOP framework'ов
  4. Выбор AOP framework'а
  5. SNAP - Simple .NET Aspect Oriented Programming

Behavior Driven Development Explained with MSpec

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

Речь пойдет о behavior driven development, идеях, подходах и используемых инструментах. Слово BDD в последнее время стало своего рода buzzword'ом. Разработчиков можно в целом разделить на тех, кому нравится BDD стиль, и те, кто, относится к нему негативно либо крайне негативно. В статье постараюсь пройти по узкой дорожке и не впасть ни в одну из крайностей. Статья ориентирована на разработчиков, которые интересуются BDD подходом, либо практикуют, либо ищут более оптимальные решения и инструменты.

Рассмотрим следующие вопросы:

  1. From unit testing to behavior specifications
  2. Использование MSpec - BDD framework in context-specification style
  3. Conclusion

NHibernate: mapping class hierarchy

Эта статья погрузит Вас в тонкие материи работы с NHibernate, а именно mapping иерархии классов в смешанном стиле table-per-subclass + table-per-hierarchy + discriminator.

В стиле table-per-subclass, равно как и table-per-hierarchy, нет ничего особенного, это все довольно стандартные и банальные вещи, если речь идет об отображении иерархии классов. Интересное начинается тогда, когда задача выходит за рамки простых учебных примеров, и нужно, например, совместить эти два стиля в рамках отображения одной иерархии классов. Как раз в этих ситуациях и проявляется истинная мощь NHibernate. Об этом мы сегодня и поговорим.

Попутно будет раскрыт ряд интересных моментов, таких как:

  • Что такое implicit polymorphism в NHibernate.
  • В чем заключается сложность mapping'a интерфейсов.
  • [BONUS] Как в NHibernate вытянуть все данные из БД в рамках одного простого запроса.
  • Как выглядит sql запрос, который сгенерирует NHibernate для иерархии классов.
  • Использование любого sql-выражения в качестве discriminator, а не только фиксированной колонки.
Mapping будет описываться c помощью Fluent NHibernate, также будут даваться ссылки на raw xml-based конструкции.