Тестирование событий с помощью Moq
Вообще название статьи опять очень длинное на самом деле, что-то в духе: Тестирование сложных сценарий с событиями на примере Moq, связыванием StructureMap, построением объектов с помощью NBuilder, и проверки условий с помощью FluentAssertions. Но согласитесь, что это как-то чересчур и надо что-то выбрать одно, а то перечислять все технологии в заголовке плохо. Особенно когда рассматриваешь пример не простого приложения типа Hello World.
При тестировании real-life приложения возникает очень много вопросов по реализации и использовании технологий, которые обычно не рассматриваются в обзорных статьях, которые копипастятся друг у друга блогерами и агрегаторами статей. Надеюсь, данная статья прольет свет на некоторые аспекты использования упомянутых технологий.
TFS2011 – Создание проекта и настройка билд-сервера
После того, как я побывал на TechEd 2011 Russia, я получил заветный купон с промо-кодом для регистрации на http://tfspreview.com, где можно попробовать новый TFS в действии как сервис. По правде сказать, TFS можно теперь поставить себе локально и смотреть на него, и любоваться. Впрочем далее по тексту вы увидите, что TFS придется качать и устанавливать, но я задействовал только контроллер сборки. Про новый TFS можно сказать много слов, но лучше всего описывает характеристика в духе: «Секси-секси!»
В этот раз я хочу рассказать о практических аспектах настройки Team Foundation Server 2011, от создания проекта до настройки билд-сервера. Вообще я изначально хотел настроить только билд-сервер, так как без него счастье было бы не полным. Дальнейшее повествование не решает каких-то супер-сложных проблем, единственно что порой некоторые моменты не совсем понятны, но для этого статья и пишется.
Далее будет много картинок и совсем немного текста.
Dapper – micro-ORM — II
Продвинутые возможности
К продвинутым возможностям можем отнести выполнение пакетной вставки данных, получение и разбор нескольких запросов за раз, множественный маппинг.
Пакетная вставка данных как пример использования списка параметров
С помощью Dapper можно осуществлять пакетную вставку данных передав список данных. Т.е. у нас есть список людей для внесения в базу данных, и с помощью единственной команды можно это провернуть. Лучше сразу на примере показать:
public void ExecuteNonSelectBulkCommand() {
con.Open();
var list = new List();
for (var i = 1; i < 5; i++) {
list.Add(new Person {
PersonId = Guid.NewGuid(),
Birth = new DateTime(1980, i, i),
Name = "Pers " + i,
});
}
con.Execute("INSERT INTO Person (PersonId, Name, Birth) VALUES(@PersonId, @Name, @Birth)"
, list);
var persons = con.Query("Select * from Person");
foreach (var p in persons)
Console.WriteLine("{0} {1} {2}", p.Name, p.Birth, p.Resident);
con.Close();
}
Создаем несколько экземпляров класса Person, и передаем список в качестве параметра в команду Execute. Потом можно визуально проверить результат.
В качестве параметра для пакетной обработки может выступать любая коллекция, которая реализует интерфейс IEnumerable<T>.
Упс!
Ничего же не менялось в коде, и все переменные указаны верно, в чем же дело?
Dapper – micro-ORM — I
Недавно я рассказывал про легковесную ORM BLToolkit, и при поиске и изучении материала неизбежно наталкивался на сравнение BLT с другими разработками в области мапирования данных на бизнес-объекты. Одним из самых привлекательных вариантов по скорости, а так же по вниманию общественности, оказался Dapper.
Dapper – это даже не легковесная, а микро-ORM система для чтения (в основном) информации из реляционных баз данных. Данная микро-ORM система является разработкой Сэма Сафрона (Sam Saffron) для Stack Overflow, где она работает в связке с Linq2Sql. Для такого большого и посещаемого ресурса как Stack Overflow очень важно быстро получать информацию из базы данных, так как большинство пользователей просматривает ответы, использует их в своей работе, и сравнительно редко пишет. Для записи информации, что требуется значительно реже, до сих пор самым удобным и быстрым остается Linq2Sql.
О системе
Dapper – это по сути один файл с исходным кодом, который надо включить в свой проект. Dapper работает в некотором роде классом помощником, расширяя стандартный интерфейс IDbConnection с помощью extended методов. Т.е. данному фреймворку абсолютно без разницы, с какой базой вы работаете, если соединение с ней построено на указанном интерфейсе.
ClickOnce, WPF, MSBuild и несколько окружений — II
Подготовка к трансформации проекта
Чтобы у нас все получилось, потребуется скачать и установить на машине билд-сервера MSBuild Community Tasks Project, так же советую его поставить и на своей машине, для экспериментов и быстрого доступа к файлу справки. Данный пакет позволяет обращаться к реализации массы наиболее часто востребованных функций во время преобразований в процессе построения приложения с помощью MSBuild. Пакет устанавливается по адресу C:\Program Files (x86)\MSBuild\MSBuildCommunityTasks, это чтобы вы быстро нашли справку по новым доступным задачам.
Канон
Так уж получается, что мы вносим серьезные изменения в процесс сборки приложения, и как все хорошие разработчики будем отделять мух от котлет, т.е. почти все существенные изменения будут производиться в отдельном файле.
MSBuild при сборке проекта руководствуется сложным набором правил и указаний, которые мы можем модифицировать или дополнять. Основные указания о сборке проекта содержаться в файле Common.CSharp.targets – который менять каким-либо образом крайне не рекомендуется. По принятому соглашению, все дополнения и расширения, касающиеся построения приложений должны иметь расширение .targets, по сути это XML файл.
ClickOnce, WPF, MSBuild и несколько окружений — I
Или сказ о том, как сделать публикацию приложений в один клик на разные окружения для тестирования разных версий приложений на WPF с помощью ClickOnce и TFS 2010.
На днях мне нужно было решить следующую, на мой взгляд, достаточно распространенную задачу, по размещению двух версий приложения. Одна версия QA – для тестирования текущих наработок, то, что реализуется каждый день: UI, логика, исправление мелких ошибок. Другая версия – Prod – для тестирования общих алгоритмов на реальных данных. В целом стандартная практика в мире разработки.
Решение этой задачи оказалось не столь стандартное и простое как я ожидал, для такой задачи. Но обо всем по порядку.
Диспозиция
В качестве исходных данных мы примем то, что у вас есть работоспособное приложение на WPF (для WinForm будет то же самое, но WPF более сложный случай), которое можно локально собрать в нескольких конфигурациях: Dev, QA(Cons), Prod. Не важно, что именно у вас зависит от конфигурации, в общем случае, скорее всего это строки соединения с базой данных, оптимизация и логирование ошибок/действий пользователя.
Так же, как дополнительное условие, все конфигурации должны собираться на билд-сервере, в данном случае на TFS и при использовании MSBuild. То, что дальше описывается можно собирать локально, с помощью командной строки. Так что TFS – усложнение задачи, которое будем принимать во внимание.
И еще у вас успешно настроена на публикацию из VisualStudio с помощью ClickOnce хотя бы одна конфигурация приложения.
Еще раз, у нас есть:
- WPF приложение
- У приложения несколько рабочих конфигураций
- Все конфигурации компилируются на билд-сервере (TFS)
- Хотя бы одна конфигурация публикуется с помощью ClickOnce из VisualStudio
Проблема
В описанной системе все хорошо, все собирается на TFS, в разных конфигурациях. Т.е. я могу в любой момент получить рабочее приложение с любой конфигурацией. Однако распространение для пользователей идет только по линии QA. До определенного этапа разработки этого хватало. Теперь же надо опубликовать Prod, при этом на одном компьютере должны одновременно стоять как QA версия, так и Prod.
Проблем с физической публикацией не возникло. Т.е. на сервер складываются разные версии приложения, но при установке пользователю, одна версия затирает другую.
Кроме этой проблемы вам придется решить еще ряд других, не менее важных задач, как например обеспечение версионности приложения.
Надеюсь, что проблема ясна, но я более подробно покажу еще раз, как приходят к этой ситуации и как ее решить во всех подробностях.
BLToolkit. Intro — II
Хранимые процедуры
Для многих разработчиков является нормой сделать всю работу с базой на хранимых процедурах, лет десять назад это было очень популярно. Впрочем, еще много ситуации когда без хранимых процедур будет тяжело обходиться, так что посмотрим, как же работать с ними с помощью BLToolkit.
Начнем с написания хранимой процедуры. Пусть нам требуется выборка людей старше определенного возраста.
create procedure Person_GetOlderThan(
@Age int
)
as
set nocount on;
select PersonId
,Name
,Birth
,Resident
,Gender
,Weight
,Height
from dbo.Person
where datediff(yy,Birth, getdate()) > @Age
BLToolkit. Intro — I
Уже достаточно давно меня интересует тема CQRS, но пока что все останавливалось на изучении документов, примеров и самому написать что-то используя подход CQRS не доводилось. В последние дни работа в этом направлении активизировалась с новой силой, и пошлел процесс по исследованию инструментов более всего пригодных для реализации этой концепции. Наверно так повлияла конференция Patterns’n’Practices, когда было объявлено что к концу года команда выпустит гайдлайны по реализации CQRS на практике.
Одной из ключевых вещей является непосредственное получение информации из базы данных без промежуточных программных слоев. Такое получение данных должно быть быстрое и простое. Простое и быстрое. По многочисленным источникам и по результатам сайта ORM Battle был выбран для экспериментов BLToolkit.
Насчет скорости работы есть диаграмма сравнения с другими фреймворками на 30 июля 2011 года:
Не могу не вспомнить фразу с презентации на NDC2011 по поводу Kill your ORM, где ведущий высказался в таком духе: «Linq2Sql вышел слишком простым и быстрым, поэтому придумали EF. Больше абстракций!».
Первой фазой я решил проверить легкость использования и различные нюансы построения классов для маппинга, так как обычно уже на этой фазе начинают вылезать разные неприятные моменты использования.
На официальном сайте есть что-то в духе вводного курса, но как обычно это и бывает, многие вещи писатели держали в уме и считали само-собой разумеющееся. Разобраться можно, но последовательность повествования надо поменять, что я и попробую сейчас сделать, надеюсь, что получится яснее.
Постоянные подписчики
При разработке сервисов для небольшой программы вы, скорее всего, не думаете о многопоточности, о том, что какой-либо метод сервиса будут запускать сразу несколько экземпляров класса и много еще о чем обычно не думаете. Так же как и я. В небольших программах, которые, скорее всего, будут исполняться в один поток, не надо развитой системы событий. Можно просто передать делегат, который будет вызван после окончания работы сервиса.
Может быть обстоятельства, а может быть что-то уложилось в голове, но описанный выше подход больше не работает в программах, которые я пишу. Слишком много действий одновременно может делать пользователь и начинка программы, чтобы оставаться в рамках модели «один сервис – один потребитель». Однако с новым подходом меняется сложность и ответственность кода, и на этом пути поджидают многие сложности и опасности.
Новый подход, как вы наверно догадались, будет состоять в построении системы на событиях (event). При первом взгляде не видно никаких проблем при использовании событий, но это только на первый взгляд. Не уверен, что можно правильно проранжировать опасности, но, пожалуй, перечислю их в той последовательности, в которой с ними сталкиваешься.
Повторно подписались. Самая частая ошибка, которая вводит в ступор людей, это когда они обнаруживают:
- кратные прибавки в вычислениях
- двойные записи в базе данных
- прочие казусы вычислений и отображений данных.
Неявной проблемой, с которой сталкиваются нескоро при отсутствии проблем с вычислениями является скорость работы приложения. При двойной, тройной и так далее подписках программа начинает делать одни и те же действия без практической необходимости, что может серьезно снизить производительность.
Забыли отписаться. Бывает так, что повторной подписки не происходит, но и отписаться от события забыли. В этом случае могут возникнуть утечки памяти, так как сервис держит класс и не дает сборщику мусора пометить объект на удаление.
К слову, не всегда бывает удобно отписываться и хочется, чтобы подписка была «слабой», как WeakReference. Тогда, если на объект нет внешних ссылок, кроме слабых подписок, то сборщик мусора пометит его на удаление, подписка освободится, память освободится и будет небольшое программистское счастье. Собственно, об этом и пойдет речь, как реализовать и использовать в своем приложении слабую подписку на события. Попутно я планирую раскрыть еще несколько рабочих моментов при использовании событий и сервисов.
Eloquera and WPF
Во время экспериментов с объектной базой Eloquera, я столкнулся с одним очень неприятным эффектом, который меня напрягал в течение месяца наверно или даже больше. За это время я успел замучать самих создателей базы, отписался на официальном форуме DevExpress, даже вовлек в эту котовасию Дона Смита после конференции Patterns’n’Practices и его коллег. В итоге, после многочисленных писем со стэк-трейсами и примерами кода, проблема была решена. Но обо всем по порядку.
Частой практикой при разработке сколько-нибудь сложного приложения является вынос всех визуальных компонентов в отдельную библиотеку. Т.е. существует исполняемый проект с основной формой и проект с пользовательскими экранами (UserControls), которые отображаются на основной форме. В простейшем случае все это выглядит примерно так в обозревателе проектов:
Используя простейший код для отображения пользовательских компонентов, можно убедиться что все хорошо, компоненты отображаются и приложение работает как ожидалось.
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
content.Children.Clear();
content.Children.Add(new ExternalDllControl());
}
}
Здесь за переменной content скрывается StackPanel, так что приводить код основной формы не стоит.
Далее начинаются волшебные вещи, которые можно было охарактеризовать фразой из старинного анекдота: «Если я тебя по голове ударю, у тебя шнурки развяжутся?», так вот шнурки развязывались. При создании экземпляра класса базы данных Eloquera, выкидывалось сообщение о невозможности найти визуальный компонент в библиотеке CustomControl. Такое поведение характерно только для работы базы в конфигурации Desktop.
The component 'CustomControls.ExternalDllControl' does not have a resource identified by the URI '/CustomControls;component/externaldllcontrol.xaml'.
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
var db = new DB("server=(local);options=none");
content.Children.Clear();
content.Children.Add(new ExternalDllControl());
}
}
Дальнейшая работа была невозможна. Ошибка происходила в независимости от того, в каком потоке создавалось соединение с базой данных.
Проблему можно было обойти перенеся все компоненты в исполяемую сборку, но это было частичное решение и работало только если не использовались визуальные компоненты третьих сторон, с которыми могли произойти те же самые сложности.
О причинах такого поведения
После долгих расспросов и копаний обнаружилось, что проблема была в механизме обнаружения хранимых процедур, которые появились в версии 4.1, но задел к которым был наверно уже с версии 3.4.1. Данный механизм автоматически сканирует библиотеки в директории по умолчанию и загружает их в пространство памяти сервера. Таким образом, сервер пытался отъесть все библиотеки, и если остальному коду было все равно, на то какой у них codebase, то с WPF такой фокус не прокатил.
Решение
Решение как всегда оказалось простое и эффективное. Если не планируется использовать хранимые процедуры для базы Eloquera, то надо указать директорию для поиска библиотек с хранимыми процедурами на какую-нибудь папку, где вашего приложения точно не окажется.
Изменить путь до хранимых процедур можно декларативно в коде, причем сделать это желательно как можно раньше, до загрузки визуальных компонентов. Я поместил код в App.
public partial class App : Application {
public App() {
var db = new DB("server=(local);options=none");
DB.Configuration.ServerSettings.StoredProceduresPath = "c:\temp";
}
}
После этого проблема исчезла и можно спокойно продолжать работу над проектом, используя в качестве базы данных Eloquera.
Hard'n'Heavy!



