Рождение новой CMS-системы часто начинается с проектирования архитектуры и реализации самых простых блоков этой архитектуры.
Сегодня, я начинаю цикл статей, посвящённых новой системе управления сайтом реализованной на языке C# и платформе .Net. Система планируется быть с открытым исходным кодом, и данными статьями, я постараюсь описать все её элементы в порядке их начального появления и проектирования ещё года 2 назад.
Началом был универсальный слой доступа к любому провайдеру данных (то ли реляционная БД, или не реляционная, или даже своя организация базы данных).
Кого заинтересовал, прошу под кат.
Как и всё, что мы хотим сделать гениальным и простым, должно быть ещё и удобным, быстрым, и гибким. Так хотелось сделать и со слоем доступа к данным. Прошу заметить, любым данным :)
Самый простой метод для программиста был всегда использование какой-либо ORM системы. В .Net Framework этого добра хватает, начиная с истоков — NHibernate, Nolics, LINQ2SQL, Entity Framework, и куча самописных… Какие шаги обычно должен сделать разработчик, чтобы подключить какую-либо ORM систему к проекту?
И всё вроде как круто. Но! Приходит время, когда заказчику не нравится субд (может она дорогая или медленная а тут появляется MongoDB которая у всех на устах). И программисту приходится переписывать и переделывать весь слой доступа к данным. Опять почти всё те же шаги (прошу не пинать меня любителей гибкой архитектуры, в которой всё уже давно предусмотрено, мы не о тех задачах пока говорим:) ).
Вот и родилась мысль унифицировать доступ к разным провайдерам предоставляя интерфейс CRUD операций и не предоставляя отношений между таблицами (не все провайдеры бд знают об отношениях, а как проделать отношения в AcroDB я расскажу в следующих статьях). Чего я хотел добиться?
Что из этого получилось? Проект AcroDB вы можете скачать с сайта github'а
Давайте посмотрим, что вам нужно для использования любой таблицы с её автоматическим созданием (подробный пример есть в подпроекте AcroDbTest в сорцах).
Первое, вам нужно описать интерфейс будующей таблицы:
помечая его аттрибутом AcroDbEntity вы указываете системе что на основе этого интерфейча нужно создать в памяти класс который его имплементирует и использовать этот класс для работы с внутренней ORM-системой. неплохо? :) идём дальше.
Далее нам нужно при запуске проекта настроить подключение к нужному провайдеру данных (пока поддерживается MsSql и MongoDB для тестирования):
Метод SettingsCallBack используется для подстановки конфигурационных данных для настройки провайдера данных. в нашем примере это строка подключения к СУБД.
Класс DataContextFactory это синглтон, который предоставляет по запросу контексты разных провайдеров данных. Для того чтобы DataContextFactory знал о новых провайдерах данных, его методу ScanAssembly нужно скормить сборку, где возможно есть информация о таком провайдере, и рефлексивно он выполнит поиск.
Теперь нам нужно настроить генератор контекстов доступа к данным на использование провайдера:
AcroDataContext тоже синглтон, а метод ScanAssemblyForEntities ищет интерфейсы помеченные аттрибутом AcroDbEntity в сборке и создаёт классы-прототипы, для использования в ORM.
Как дальше пользоваться CRUD операциями:
Опишу тут только одну строку — AcroDataContext.Go. Это создание контекста подключения к провайдеру данных уже подключённого и готового к работе. Не забывайте его закрывать методом Dispose.
Для того, чтобы на основе описания вашего интерфейса в провайдере данных было создано описание таблицы (в данном случае в субд будет создана таблица Users с первичным ключём ID и полем Name с типом nvarchar(255)), нужно после сканирования сборок на интерфейсы вызвать метод «AcroDataContext.PerformMigrations()». Миграции делаются на основе SubSonic Migrations. Раньше они делались с помощью SQL SMO.
На этом не заканчивается функциональность данной библиотеки, но я с радостью опишу дополнительные элементы, если кому-то это будет нужно. А также, нетерпеливые могут прочитать исходный код :)
За первую статью прошу сильно не пинать, постараюсь ответить на все вопросы :)
Сегодня, я начинаю цикл статей, посвящённых новой системе управления сайтом реализованной на языке C# и платформе .Net. Система планируется быть с открытым исходным кодом, и данными статьями, я постараюсь описать все её элементы в порядке их начального появления и проектирования ещё года 2 назад.
Началом был универсальный слой доступа к любому провайдеру данных (то ли реляционная БД, или не реляционная, или даже своя организация базы данных).
Кого заинтересовал, прошу под кат.
Как и всё, что мы хотим сделать гениальным и простым, должно быть ещё и удобным, быстрым, и гибким. Так хотелось сделать и со слоем доступа к данным. Прошу заметить, любым данным :)
Самый простой метод для программиста был всегда использование какой-либо ORM системы. В .Net Framework этого добра хватает, начиная с истоков — NHibernate, Nolics, LINQ2SQL, Entity Framework, и куча самописных… Какие шаги обычно должен сделать разработчик, чтобы подключить какую-либо ORM систему к проекту?
- Выбор бд (обязательно реляционной)
- Выбор ORM-системы для работы с бд
- Создание таблиц и связей в бд
- Создание прототипов таблиц в виде классов в коде (у LINQ2SQL этот шаг можно автоматизировать утилитой sqlmetal, а EF вообще сам делает это)
- Для таких систем как Nolics.net нужен ещё шаг описания интерфейса для прототипа таблицы и описание структуры таблицы в его собственном языке, но отпадает нужда создавать таблицу в бд ручками. Nolics при старте сделает автоматическую миграцию, если его об этом хорошо попросить :)
- Дальше разработчик пишет классы управления данными, как добавление, редактирование, удаление, выборка
И всё вроде как круто. Но! Приходит время, когда заказчику не нравится субд (может она дорогая или медленная а тут появляется MongoDB которая у всех на устах). И программисту приходится переписывать и переделывать весь слой доступа к данным. Опять почти всё те же шаги (прошу не пинать меня любителей гибкой архитектуры, в которой всё уже давно предусмотрено, мы не о тех задачах пока говорим:) ).
Вот и родилась мысль унифицировать доступ к разным провайдерам предоставляя интерфейс CRUD операций и не предоставляя отношений между таблицами (не все провайдеры бд знают об отношениях, а как проделать отношения в AcroDB я расскажу в следующих статьях). Чего я хотел добиться?
- Создать гибкий движок-замену слою доступа к данным для среднего размера проектов и малых тоже
- Создать метод описания таблицы только один раз и в одном месте
- Чтобы использование ORM было скрыто от разработчика, и запросы проводились только выражениями LINQ
- Чтобы при отстутствии описания таблицы в провайдере данных (субд) автоматически при запуске создавалась данная таблица, без участия разработчика
Что из этого получилось? Проект AcroDB вы можете скачать с сайта github'а
Давайте посмотрим, что вам нужно для использования любой таблицы с её автоматическим созданием (подробный пример есть в подпроекте AcroDbTest в сорцах).
Первое, вам нужно описать интерфейс будующей таблицы:
[AcroDbEntity]
public interface IUser
{
Guid ID { get; set; }
string Name { get; set; }
}
* This source code was highlighted with Source Code Highlighter.
помечая его аттрибутом AcroDbEntity вы указываете системе что на основе этого интерфейча нужно создать в памяти класс который его имплементирует и использовать этот класс для работы с внутренней ORM-системой. неплохо? :) идём дальше.
Далее нам нужно при запуске проекта настроить подключение к нужному провайдеру данных (пока поддерживается MsSql и MongoDB для тестирования):
static string[] SettingsCallBack(string name)
{
if (name == "MsSql")
return new[] { @"server=OLEKSIY-PC\SQLEXPRESS;Database=acrodbtest;Trusted_Connection=True;" };
return new string[0];
}
static void Main()
{
DataContextFactory.SettingsCallback = SettingsCallBack;
DataContextFactory.Instance.ScanAssembly(typeof(MsSqlDataContext).Assembly);
//...
}
* This source code was highlighted with Source Code Highlighter.
Метод SettingsCallBack используется для подстановки конфигурационных данных для настройки провайдера данных. в нашем примере это строка подключения к СУБД.
Класс DataContextFactory это синглтон, который предоставляет по запросу контексты разных провайдеров данных. Для того чтобы DataContextFactory знал о новых провайдерах данных, его методу ScanAssembly нужно скормить сборку, где возможно есть информация о таком провайдере, и рефлексивно он выполнит поиск.
Теперь нам нужно настроить генератор контекстов доступа к данным на использование провайдера:
AcroDataContext.DefaultDataContext = DataContextFactory.Instance.Get("MsSql");
AcroDataContext.ScanAssemblyForEntities(Assembly.GetExecutingAssembly());
* This source code was highlighted with Source Code Highlighter.
AcroDataContext тоже синглтон, а метод ScanAssemblyForEntities ищет интерфейсы помеченные аттрибутом AcroDbEntity в сборке и создаёт классы-прототипы, для использования в ORM.
Как дальше пользоваться CRUD операциями:
using (var manager = AcroDataContext.Go)
{
var usr = manager.Provide<IUser>().Create();
usr.Name = "Some user name";
manager.Provide<IUser>().Save(usr);
manager.SubmitChanges();
}
* This source code was highlighted with Source Code Highlighter.
Опишу тут только одну строку — AcroDataContext.Go. Это создание контекста подключения к провайдеру данных уже подключённого и готового к работе. Не забывайте его закрывать методом Dispose.
Для того, чтобы на основе описания вашего интерфейса в провайдере данных было создано описание таблицы (в данном случае в субд будет создана таблица Users с первичным ключём ID и полем Name с типом nvarchar(255)), нужно после сканирования сборок на интерфейсы вызвать метод «AcroDataContext.PerformMigrations()». Миграции делаются на основе SubSonic Migrations. Раньше они делались с помощью SQL SMO.
На этом не заканчивается функциональность данной библиотеки, но я с радостью опишу дополнительные элементы, если кому-то это будет нужно. А также, нетерпеливые могут прочитать исходный код :)
За первую статью прошу сильно не пинать, постараюсь ответить на все вопросы :)