Violet Tape некоторые мысли о разработке на платформе .Net

17Май/120

Procedure Façade

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

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

Идея

Общая идея совсем не нова и основывается на принципе Convention over Configuration. Т.е. все работает «как бы магическим образом», основываясь только на специальных правилах именования. Когда-то давно это не казалось  хорошей идеей, однако такая договоренность значительно упрощает жизнь и сейчас данный принцип используется во многих проектах и фреймворках. Взять хотя бы тот же ASP.NET MVC, который полностью построен на специальных названиях, папках и тому подобное, никто ведь не жалуется, даже скорее наоборот. Конечно, работа проектов на соглашениях кажется «магией», пока не начинаешь работать с ними и глубоко разбираться в механизмах лежащих глубоко внутри. Впрочем, для правильного и эффективного использования в любом случае придется овладеть «магией», которая превратится в простую ловкость рук.

27Мар/120

Фасад доступа к данным — Оптимизация, Заключение

Построение запросов

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

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

Linq2Sql позволяет гибко работать с запросами в стиле fluent interface переводя их в sql запросы. Было бы упущением не воспользоваться этой возможностью в полной мере. Особенно учитывая то, что мы уже работаем с логическими таблицами L2S, которые реализуют интерфейс IQueryable. Да, когда мы в адаптере чтения обращаемся к контексту с методом GetTable<TData>(), мы получаем объект, который можно донастраивать с помощью методов Where(), разбирая пользовательские спецификации. Собственно в этом и состоит идея.

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

public interface IQueryBuilder<TDomain, TData> {
    IQueryable<TData> Optimize(IQueryable<TData> query, Specification<TDomain> specification);
}

Модифицировали запрос, если надо, и отдали его обратно. Отлично, теперь рассмотрим как это может быть реализовано.

26Мар/120

Фасад доступа к данным — Адаптер записи, Трансляторы

Адаптеры записи

Как и в случае с адаптерами для чтения, большую часть работы можно вынести в абстрактный класс. Интерфейс IWriteAdapter необходим опять же в целях обнаружения классов.

Если вы взглянете на метод InfrastructureFacade.Commit() то увидите, что получение всех адаптеров для записи получается из StructureMap скопом по одному только интерфейсу, что весьма удобно, так как они нужны всегда все разом для определения очередности работы.

IWriteAdapter

В интерфейс вынесены метод и свойство необходимые для работы в классе фасада, а именно Save() и Sequence.

public interface IWriteAdapter {
	void Save(UnitOfWork unitOfWork);
	int Sequence { get; }
}

Теперь можно переходить к рассмотрению «мяса», основной работы по сохранению данных.

25Мар/120

Фасад доступа к данным — Фасад и Адаптер чтения

Фасад

Теория

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

namespace Domain {
public interface IInfrastructureFacade {
    IUnitOfWork UnitOfWork { get; }
    List<T> Get<T>(Specification<T> specification);
    void Commit(IUnitOfWork unitOfWork);
}
}

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

Про Unit of Work (UoW) рассказано было ранее, паттерн Specification так же уже рассматривался, так что на этих типах данных останавливаться не будем.

Общий принцип работы такой:

  • Получаем фасад;
  • Вызываем метод получения доменных данных, в который передаем ограничивающие условия, которым должны соответствовать элементы;
  • Что-то делаем с данными, работаем с UoW;
  • Вызываем метод сохранения UoW.
23Мар/122

Фасад доступа к данным — Подготовка

На основе LINQ to SQL

Сложность по шкале Microsoft 300-400

Сразу признаюсь, что я люблю Linq2Sql и не люблю Entity Framework. Люблю компактные API и не люблю развесистые классы с кучей методов служебных выставленных наружу. Может по этим причинам, а может быть по каким-то еще, но я использую схему доступа к данным, которую собираюсь описать ниже, уже несколько лет. По большей части меня она радует и выполняет свои задачи хорошо для большинства заданий возникающих на работе.

Вводная часть

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

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

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

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

var facade = new InfrastructureFacade();
var customers = facade.Get(Specification<Customer>.Null);

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

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

6Фев/122

Целые vs GUID vs Естественные и Суррогатные ключи

 

В мире баз данных есть несколько аргументов, которые провоцируют очень сложный выбор правильного первичного ключа из вариантов представленных в заголовке. Что будет лучше, выбрать естественный или суррогатный ключ? Если мы выбираем суррогатный ключ то, что выбрать: целочисленный ряд или же GUID? В прошлом мне пришлось с жаром отстаивать  все точки зрения, так что могу рассмотреть плюсы и минусы каждого подхода.

Естественный vs Суррогатный ключи

Джо Целко (Joe Celko) утверждает что мы должны использовать естественные ключи, созданные из атрибутов записи, а не заниматься присвоением произвольных значений записям в таблицах. Я согласен с ним. Но! В то же время мы должны стараться хранить ключи легкими/маленькими, вместо того, чтобы создавать массивные составные ключи, чьи значения в свою очередь должны будут храниться в других таблицах для возможности соединения таблиц, поддержания их целостности. Как я выбираю какой способ использовать? Легко, я использую оба.

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

Метки записи: , , Читать полностью
16Ноя/110

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>.

Упс!

Ничего же не менялось в коде, и все переменные указаны верно, в чем же дело?

Метки записи: , Читать полностью
14Ноя/110

Dapper – micro-ORM — I

Недавно я рассказывал про легковесную ORM BLToolkit, и при поиске и изучении материала неизбежно наталкивался на сравнение BLT с другими разработками в области мапирования данных на бизнес-объекты. Одним из самых привлекательных вариантов по скорости, а так же по вниманию общественности, оказался Dapper.

Dapper – это даже не легковесная, а микро-ORM система для чтения (в основном) информации из реляционных баз данных. Данная микро-ORM система является разработкой Сэма Сафрона (Sam Saffron) для Stack Overflow, где она работает в связке с Linq2Sql. Для такого большого и посещаемого ресурса как Stack Overflow очень важно быстро получать информацию из базы данных, так как большинство пользователей просматривает ответы, использует их в своей работе, и сравнительно редко пишет. Для записи информации, что требуется значительно реже, до сих пор самым удобным и быстрым остается Linq2Sql.

О системе

Dapper – это по сути один файл с исходным кодом, который надо включить в свой проект. Dapper работает в некотором роде классом помощником, расширяя стандартный интерфейс IDbConnection с помощью extended методов. Т.е. данному фреймворку абсолютно без разницы, с какой базой вы работаете, если соединение с ней построено на указанном интерфейсе.

Метки записи: , Читать полностью
3Ноя/110

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
Метки записи: , , , Читать полностью
1Ноя/110

BLToolkit. Intro — I

Уже достаточно давно меня интересует тема CQRS, но пока что все останавливалось на изучении документов, примеров и самому написать что-то используя подход CQRS не доводилось. В последние дни работа в этом направлении активизировалась с новой силой, и пошлел процесс по исследованию инструментов более всего пригодных для реализации этой концепции. Наверно так повлияла конференция Patterns’n’Practices, когда было объявлено что к концу года команда выпустит гайдлайны по реализации CQRS на практике.

Одной из ключевых вещей является непосредственное получение информации из базы данных без промежуточных программных слоев. Такое получение данных должно быть быстрое и простое. Простое и быстрое. По многочисленным источникам и по результатам сайта ORM Battle был выбран для экспериментов BLToolkit.

Насчет скорости работы есть диаграмма сравнения с другими фреймворками на 30 июля 2011 года:

Не могу не вспомнить фразу с презентации на NDC2011 по поводу Kill your ORM, где ведущий высказался в таком духе: «Linq2Sql вышел слишком простым и быстрым, поэтому придумали EF. Больше абстракций!».

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

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

Метки записи: , , , Читать полностью