Постоянные подписчики
При разработке сервисов для небольшой программы вы, скорее всего, не думаете о многопоточности, о том, что какой-либо метод сервиса будут запускать сразу несколько экземпляров класса и много еще о чем обычно не думаете. Так же как и я. В небольших программах, которые, скорее всего, будут исполняться в один поток, не надо развитой системы событий. Можно просто передать делегат, который будет вызван после окончания работы сервиса.
Может быть обстоятельства, а может быть что-то уложилось в голове, но описанный выше подход больше не работает в программах, которые я пишу. Слишком много действий одновременно может делать пользователь и начинка программы, чтобы оставаться в рамках модели «один сервис – один потребитель». Однако с новым подходом меняется сложность и ответственность кода, и на этом пути поджидают многие сложности и опасности.
Новый подход, как вы наверно догадались, будет состоять в построении системы на событиях (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!
Eloquera V
краткое описание / немного истории / установка / режимы работы базы: service, inmemory, desktop / восстановление базы
конфигурация / программная конфигурация / простейшие операции CRUD / insert / update / refreshMode / delete
построение запросов / joins / параметры / массивы / шаблонные контейнеры / order by / доступные функции / almos
хранимые процедуры
родные и динамические объекты / работа с массивами / контейнеры / фильтрация типов, свойств и полей / backup and restore / эволюция типов / fulltext search / индексация / под капотом
«Родные» и динамические объекты
Под родными объектами в данном случае подразумеваются любые типы принадлежащие CLR или созданные вами. Eloquera может на лету (в runtime) конвертировать динамические объекты в «родные» и обратно.
Внимание!
Для использования этого функционала должно быть открыто соединение к базе.
Основные методы для конвертации:
- ToNative<ConcretType>()
- FromNative(variable)
Пример для уже открытой базы по созданию динамического объекта:
Book book = new Book { DbID = 12345, Title = "Title Mitle" }
Dynamic dynObj = new Dynamic(book);
db.Store(dynObj);
Book newBook = dynObj.ToNative();
При обновлении данных применяются следующие схемы работы:
- Обновление через родной тип
0 Динамический тип переводим в «родной» тип
0 Изменяем «родной» тип
0 Обновляем (синхронизируем) динамический тип с помощью FromNative
- Обновление через динамический тип
0 Существует переменная конкретного типа с данными динамического
0 Изменяем динамический тип
0 Обновляем (синхронизируем) «родной» тип с помощью ToNative<T>(variable)
Eloquera IV
краткое описание / немного истории / установка / режимы работы базы: service, inmemory, desktop / восстановление базы
конфигурация / программная конфигурация / простейшие операции CRUD / insert / update / refreshMode / delete
построение запросов / joins / параметры / массивы / шаблонные контейнеры / order by / доступные функции / almos
хранимые процедуры
Хранимые процедуры
Начиная с версии 4.0 Eloquera поддерживает хранимые процедуры. Я думаю, не стоит расписывать все прелести и возможности хранимых процедур. Те, кто работают с базами и так их знают. Хочу только заметить, что результат выполнения хранимой процедуры остается, связан с контекстом базы, а значит, полученные данные можно изменять и обновлять объекты в базе.
Хранимые процедуры могут рассматриваться как методы расширения к основной базе. Они пишутся с использованием C#, за что благодарность создателям, за то, что они не выдумали какой-либо свой интерпретируемый диалект для этого дела. Получается, что можно использовать все возможность .Net при написании процедур.
Итак, хватит слов, лучше показать, как создаются хранимые процедуры на деле.
Первым делом надо объявить интерфейс с желаемыми процедурами. Данный интерфейс должен находиться на стороне клиента и сервера.
public interface ICityStatisticSP{
CityCars GetCityCars(string cityName);
}
Затем создаем класс, который нужен будет только на стороне сервера. При этом библиотеку с реализацией процедуры надо поместить в папку Lib в директории базы, либо настроить путь до библиотек с хранимыми процедурами с помощью параметра StoredProceduresPath.
public class CityStatisticSP: StoredProcedure, ICityStatisticSP {
public CityCars GetCityCars(string cityName) {
Parameters parms = db.CreateParameters();
parms["city"] = cityName;
var cars = db.ExecuteQuery("SELECT Car FROM Car JOIN Seller ON Car.SellerID = Seller.ID WHERE Seller.City = @city", parms)
.OfType()
.ToList();
…
}
}
Добавление новых процедур не требует перезапуска сервиса базы данных.
Eloquera III
краткое описание / немного истории / установка / режимы работы базы: service, inmemory, desktop / восстановление базы
конфигурация / программная конфигурация / простейшие операции CRUD / insert / update / refreshMode / delete
построение запросов / joins / параметры / массивы / шаблонные контейнеры / order by / доступные функции / almos
Построение запросов
Eloquera позволяет писать запросы к базе данных на привычном многим SQL, причем с приятными дополнениями, которые я не встречал в MS SQL. Запросы можно писать в отношении полей и свойств класса, к дочерним объектам, коллекциям и массивам объектов. Все вместе это дает очень мощную платформу для анализа данных.
Как вы уже могли заметить, получение данных происходит с помощью двух функций:
- ExecuteQuery(string query)
- ExecuteScalar(string query)
Обе функции имеют перегруженные варианты для возможности указания глубины запрашиваемых объектов, параметров. Да-да, можно строить параметризированные запросы.
Общий вид и порядок служебных слов в запросе тот же, что и в «обычном» SQL . Если делается запрос к одной сущности, то слово from можно опустить. Например:
var result = eloquera.ExecuteScalar("Select ClassId where Text = 'XXX'");
Eloquera поддерживает операторы TOP, SKIP, Order by (подробно будет чуть позже).
var result = eloquera.ExecuteQuery("Select Top 5 ClassId");
var result = eloquera.ExecuteQuery("Select Skip 3 ClassId");
Запросы можно строить не только для собственных полей класса, но и обращаться к составным полям, массивам. Сложный объект может содержать другие объекты или массивы, например:
public class BasicUser {
public Location HomeLocation;
public Location CurrentLocation;
public string Name;
public DateTime BirthDate;
public string Interests;
public BasicUser[] Friends;
public WorkPlace UserWorkPlace;
public School UserSchool;
public string[] Emails;
}
Eloquera II
краткое описание / немного истории / установка / режимы работы базы: service, inmemory, desktop / восстановление базы
конфигурация / программная конфигурация / простейшие операции CRUD / insert / update / refreshMode / delete
Конфигурация
Файл конфигурации в поставке для автономной работы (Desktop mode) по умолчанию выглядит следующим образом:
<?xml version="1.0"?>
<Eloquera xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Cache IndexCacheSize="1000"
WriteThru="false"
IndexCleanerPeriod="100"
IndexCleanerBatchSize="100"
CleanerPeriod="100"
CleanerBatchSize="2000"
CommitSequenceMaxLength="2000"
ShallowReadThreshold="1000"
ShallowReadAhead="1000"
ReadAhead="20"
SaturationThreshold="10000"
MemoryFootPrint="1000"/>
<MemoryManager Mode="1" />
<Server ServerPort="43962"
Trace="true"
InMemoryAllowed="true"
Secure="false"
AllowUsers=""
AllowGroups="Everyone" />
<SmartRuntime Smart="true" />
<UserLogin Enabled="false"
PasswordHash="l+on1aCwDrcZ5bGlv+fyyIlYkbuFIOxZFlFwIGKlms0CCwoGn9TZvM0E3Uksjwx64+/yv8nsaUajWLz1kyKG7A==" />
</Eloquera>
Прошу обратить внимание на то, что путь для баз данных не указан. Для корректной работы надо будет его дописать, используя атрибут DatabasePath в узел Server.
Внимание!
С версии 4.0 можно полностью игнорировать файл настройки и задание директории для хранения баз данных. Eloquera самостоятельно все определит и настроит.
Eloquera I
краткое описание / немного истории / установка / режимы работы базы: service, inmemory, desktop / восстановление базы
Краткое описание базы
База данных Eloquera с самого начала была написана для хранения объектов на основе .Net Framework, что сделало возможным попытаться вобрать в себя все лучшее от объектных и реляционных баз данных одновременно, преодолев многие их различия. Теоретически Eloquera может работать с любыми объектами из семейства .Net Framework, однако на практике работа проверялась пока только с C#. Главная ориентированность разработчиков на enterprise сегмент, а не на embedded решения.
Отличительные особенности Eloquera весьма внушительны и постоянно пополняются, вот их примерный список:
- Сохраняет C# объекты (любые объекты любого языка на .Net платформе) без необходимости реализации специальных интерфейсов и адаптеров.
- Сохраняет Dynamic объекты с любыми полями\свойствами и может сопоставить их объектам любого типа.
- Язык запросов максимально приближен к SQL, при этом не требуется наличие какой либо реляционной SQL базы.
- Поддержка LINQ.
- Возвращает объекты в том виде, в котором они были сохранены (включая перечислимые типы)
- Поддержка параметров в виде списков и массивов. В свою очередь массивы могут быть многомерными – синтаксис запросов остается неизменным.
- Поддержка функций и выражений в запросах и конструкции ORDER BY.
- Стандартный и расширенный ORDER BY.
- Регулярные выражения в запросах.
- Индексация и оптимизация запросов.
- Пакетная вставка и обновление данных.
- Поддержка шаблонных объектов.
- Восстановление Read-only полей и свойств.
- Учет классов наследников в запросах. Select ParentClass вернет и ParentClass и ChildClass.
- Возможность построения запросов относительно указанного типа. Select ONLY ParentClass вернет только ParentClass без ChildClass.
- Поддерживается частичный возврат объектов. Например, если вам требуется класс ForumTopic, тогда можно не подтягивать все ссылки на ForumMessages.
- Можно указать глубину объекта для возврата в запросе.
- Клиент-серверная архитектура.
- Возможность одновременной работы с несколькими пользователями. Запросы выполняются одновременно и независимо.
- Аутентификация через Windows аккаунты или через механизмы Eloquera.
- Поддержка х86 и х64.
- Уникальные идентификаторы для каждого объекта – отлично подходит для работы в системах не хранящих состояние и ASP.NET.
- Поддержка культур. Например WHERE dates BETWEEN['en-US'] @d1 and @d2 будет интерпретировано в US формате, даже если текущий язык системы русский.
- Нестрогий поиск c использованием конструкции ALMOST.
- Хранимые процедуры.
Postsharp: авторизация и аутентификация
Одно из самых востребованных применений аспектно-ориентированного программирования это вынос обслуживающего инфраструктурного кода, который часто повторяется в системе, в отдельный класс. Что в свою очередь является проявленем принципа единой ответственности (SRP - Single Responsibility Principle). Очень часто задачи авторизации и аутентификации разбросаны по всему коду проверяя права доступа пользователя. Следствием этого является большая трудоемкость изменений в этой немаловажной части логики, а так же ее общая проверка. Принцип единой ответственности говорит о том, что должна быть только одна причина для изменения класса, так что все что относится к авторизации и аутентификации просто просится в отдельные классы.
Основные методы аутентификации обычно не то, что используется по всему приложению. Например в веб приложениях ввод данных для аутентификации и сама аутентификация происходит на одной странице, после чего информация о пользователе хранится в каком-либо токене со сроком годности. Благодаря этому пользователь может автоматически входить в систему в течении срока работы токена. Таким образом единственным кодом, который пронизывает все страницы приложения будет проверка того, что пользователь до сих пор в системе. Конечно, вы можете использовать PostSharp для такой проверки, но это не лучший способ применения, по моему мнению.
Wiki
Небольшое вступление и пояснение к некоторым будущим записям из рубрики Процесс. С недавнего времени я начал работать в компании, где надо полностью поднять весь процесс разработки ПО. Заметки по ходу этого действа будут появляться чаще и порой без кода совсем. У меня просьба к читателям высказывать свое мнение и наработки, по освещаемым вопросам. Ну и я надеюсь, что в результате такой работы будет много интересного материала и пищи для размышления.
Еще из необходимого ПО я считаю надо ставить wiki. К сожалению, предыдущий опыт не был сильно удачен. До этого я пробовал ставить википедии с помощью WebPlatform, насчет самой платформы нареканий нет, но представленные на тот момент википедии удручали. Одна не смогла доставить MySQL, да и сам MySQL не особенно желанный гость на моей машине. Второй движок ScrewWiki – вполне оправдывает свое название. Нифига не работает, редактирование паршивое, оформление тоже. Было решено отказаться от такого счастья. Поиск других движков не выглядел радужной перспективой и на какой-то момент я бросил это дело.
На днях снова заглянул в Web Platform Installer и обнаружил там новый движок для википедии, блогов, рассылок и чего-то в духе твиттера. Называется Incentive. До 10 зарегистрированных пользователей бесплатно.
Правила оформления кода C# v1.0
Волею судеб я попал в "девственные леса" организации, где не было разработок на C# и надо поставить все процессы с нуля. Систему контроля версий, процесс работы, сбор требований и учет работы. Правила оформления кода. Как раз первую версию такого документа я представляю на ваш суд. Я руководствуюсь исключительно своими предпочтениями в оформлении кода, которые позволяют мне быстро и эффективно ориентироваться по коду приложения.
Здесь отражено только форматирование кода, нет указаний на code smell или какие-либо еще недочеты в самом коде.
Если есть что добавить по существу, то прошу обсудить это в комментариях.
Краткое содержание:
- Переменные
- Использование VAR
- Именование методов
- Обрамление кода
- Комментарии
- Оформление блоков кода
- Свойства
- Логические операции
- Организация класса
