Несколько лет назад я реализовал ряд проектов, для управления которыми использовалась система управления основанная на ASP.NET Dynamic Data. В свое время эта система сэкономила достаточно много времени и ресурсов. Но как известно, в ИТ все развивается очень стремительно. Не так давно вышла в релиз платформа .NET Core, основным нововведением которой была поддержка кроссплатформенности. Это в свою очередь позволило мне мигрировать ряд небольших проектов, а также проектов, которые я поддерживаю на некоммерческой основе на бюджетные сервера от Digital Ocean, которые, как известно, поддерживают только ОС семейства Linux. Когда дело дошло до системы управления передо мной стоял выбор — с минимальным изменением кода портировать проект под Mono, или переписать с нуля использую новые возможности .NET Core. Взвесив все за и против, я выбрал второй вариант. Что из этого вышло и что я собираюсь получить вы можете узнать под катом.



Для для тех кто не в курсе, что такое скаффолдинг, краткое описание этого подхода:
"Скаффолдинг — это метод метапрограммирования для создания веб-приложений, взаимодействующих с базой данных. Этот метод поддерживается некоторыми современными MVC-фреймворками (Grails, Yii, Ruby on Rails, Django, CakePHP, Phalcon PHP, Symfony, ASP.NET Dynamic Data и другими.). Разработчик в них задает спецификации, по которым в дальнейшем генерируется программный код для операций создания определенных записей в базе данных, их чтения, обновления и удаления."[1]

Вдохновившись примером Ruby on Rail в 2007 году компания Майкрософт разработала свой инструмент для быстрого проектирования веб-приложения работающих с данными.

"ASP.NET Dynamic Data – это фреймворк, который позволяет быстро разрабатывать полнофункциональные data-driven приложения, используя LINQ to SQL или Entity Framework, а также расширяет возможности элементов управления DetailsView, FormView, GridView и ListView в плане функциональности, проверки данных и отображения.
Если говорить по простому, то Dynamic Data предназначен для быстрой генерации фронт-эндов для баз данных Microsoft SQL Server."[2]

И сам подход и инструмент ASP.NET Dynamic Data мне понравились. Причем на столько, что как я уже писал выше, я создал свою систему на их основе. Однако, за время использования этого решения у меня появляли различные идеи о том, как можно было бы улучшить этот продукт. И так, к тому, чтобы переписать проект с нуля меня подтолкнул ряд недостатков решения на базе ASP.NET Dynamic Data, среди которых основные:

  • Построение модели не напрямую из базы данных, а по сгенерированной модели Entity Framework, что в свою очередь влечет необходимость переброски проекта, содержащего модель в случае изменения структуры базы.
  • Проект был основан на устаревшей на сегодняшний день технологии WebForms
  • Поддерживался только SQL Server

Исходя из этих недостатков я выделил для себя ряд возможностей, которые я бы хотел реализовать в новом проекте:

  • Поддержка кроссплатформенности
  • Поддержка различных баз данных (на данный момент реализована поддержка SQL Server и идет работа над реализацией поддержки MySQL. В планах также: PostgreSQL, Oracle Database, SQLite).
  • Генерация модели базы данных напрямую из базы.
  • Lookup с расширенными возможностями поиска

Общий принцип работы системы


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

Процедура работы следующая:

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

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

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

  4. После того, как модель построена, в принципе, с системой можно уже работать. Однако для полностью комфортной работы может понадобиться уточнить параметры некоторых полей. Чтобы упростить процедуру поддержки конфигурационного файла в будущем, я предусмотрел возможность описывать уточненную конфигурацию в отдельном файле db_ex.json. Это файл по структуре аналогичен db.json, но в нем необходимо указывать только имя поля, которое требует дополнительной настройки и указать те параметры, которые отличаются от сгенерированных по умолчанию. Разбиение конфигурации на два отельных файла позволяет не беспокоиться о том, что если в будущем придется обновить модель данных то все кастомные настройки будут потеряны. А это может произойти, например, если в таблицу были добавлены новые поля, или была создана новая таблица в базе.




Конфигурация

  • db.json – файл с описанием схемын базы данных
  • db_ex.json – файл с расширенным описания схемы. Позволяет переопределять типы полей и другую мета-информацию.
  • configuration.json – файл содержащий информацию о подключении к базе данных, пользователям системы и информацию для подключения к файловым хранилищам




Структура описания базы
Схема

  • Name – системное название схемы.
  • Title – заголовок. Отображается в интерфейсе системы
  • Tables – список таблиц


Таблица

  • ShowInList – параметр указывает, будет ли отображаться таблица в навигационном меню
  • Name – системное имя таблицы. Соответствует имени таблицы в базе данных.
  • Title – заголовок таблицы. Оторажется в навигационном меню и на странице редактирования таблицы.
  • Description – описание. Отображается на странице редактирования таблицы
  • Columns – список полей


Поле

  • Position – позиция поля в гриде и редакторе.
  • IsKey – является ли поле ключевым полем в базе данных
  • IsNullable – допустимы ли NULL-значения в поле
  • Reference – ...
  • ShowInGrid – отображать, или не поле в гриде
  • AutoIncrement – является ли поле автоинкрементным
  • MaxValue – максимальное значение поля
  • MinValue – максимальное значение поля
  • MaxLength – максимальная длинна поля
  • Readonly – является ли поле доступным только для чтения
  • Type – тип поля
  • Name – системное название поля, соответствует имени поля в таблице базы данных
  • Title – название поля, тображается в редакторе и гриде
  • Description – описание поля


Поддержка типов данных

На данный момент в системе предусмотрены (но еще не все реализованы) следующие редакторы для различных типов полей:
  • Text — обычное текстовое поле
  • Email — поле для ввода почтового адреса
  • Url — поле для ввода url
  • Phone — поле для ввода телефона
  • HTML — поле содержащее полноценный WYSIWYG редактор.
  • Password — поле для ввода пароля
  • Date — поле для ввода даты
  • Time — поле для ввода времени
  • DateTime — поле для ввода времени и даты
  • File — поле для загрузки файла и сохранения идентификатора загруженного файла в таблицу
  • Integer — поле для ввода целых чисел
  • Double — поле для ввода дробных чисел
  • Image — поле для загрузки изображения, предпросмотра загруженного изображения и сохранения идентификатора загруженного изображения в таблицу
  • Binary — поле для загрузки файла и сохранения содержимого в таблицу,
  • Reference — выпадающий список, или всплывающее окно с возможностью поиска для выбора связанного значения
  • Boolean — чекбокс



Пример файла конфигурации db.json
{
  "Tables": [    
    {
      "Columns": [
        {
          "Position": 1,
          "IsKey": true,
          "IsNullable": false,
          "Reference": null,
          "ShowInGrid": false,
          "AutoIncrement": true,
          "MaxValue": null,
          "MinValue": null,
          "MaxLength": null,
          "Readonly": true,
          "Type": 40,
          "Name": "Id",
          "Title": "Id",
          "Description": ""         
        },
        {
          "Position": 2,
          "IsKey": true,
          "IsNullable": false,
          "Reference": null,
          "ShowInGrid": false,
          "AutoIncrement": true,
          "MaxValue": null,
          "MinValue": null,
          "MaxLength": null,
          "Readonly": true,
          "Type": 40,
          "Name": "Name",
          "Title": "Name",
          "Description": ""        
        },
        {
          "Position": 3,
          "IsKey": true,
          "IsNullable": false,
          "Reference": null,
          "ShowInGrid": false,
          "AutoIncrement": true,
          "MaxValue": null,
          "MinValue": null,
          "MaxLength": null,
          "Readonly": true,
          "Type": 40,
          "Name": "Value",
          "Title": "Value",
          "Description": ""            
        }
      ],
      "ShowInList": false,
      "Name": "Setting",
      "Title": "Настройки",
      "Description": "Системные настройки"
    }
  ],
  "Generated": "2016-09-27T00:40:48.9189786+03:00",
  "ExtendedConfigurationLoaded": false,
  "Name": "Database",
  "Title": "Database"  
}



Пример файла конфигурации db_ex.json
{
  "Tables": [    
    {
      "Columns": [
        {
          "Type": 40,
          "ShowInGrid": false,
          "AutoIncrement": true,          
          "Readonly": true,
          "Name": "Id",
          "Title": "Системный идентификатор"          
        },
        {
          "Type": 10,                    
          "Name": "Name",
          "Title": "Название"          
        },
        {
          "Type": 10,                    
          "Name": "Value",
          "Title": "Значение"          
        }
      ],
      "ShowInList": false,
      "Name": "Setting",
      "Title": "Настройки",
      "Description": "Системные настройки"
    }
  ],
  "Name": "Описание проекта",
  "Title": "База данных"  
}



Пример файла конфигурации configuration.json
{
    "ConnectionString": "Server=...;Database=...;User Id=...;Password=..;",
    "SecretKey": "secret-key-123",
    "ApplicationRestartCommand": "",
    "StorageConfiguration": {
        "Type": 0,
        "Url": "http://static.exapmle.com/user_upload/",
        "Connection": {
            "Path": "/var/www/example.com/user_upload/"
        }
    },
    "Users": [{
            "Login": "admin",
            "Password": "admin",
            "Administrator": true
        }       
    ]
}



Файловые хранилища


  • FileSystem – наиболее частый вариант, когда файловое хранилище располагается на том же сервере, где запущена система управления. В этом случае указаывается путь к файлу.
  • FTP – файловое хранилище располагается на удаленном сервере, загрузка файлов происходит по протоколу FTP(реализация в планах)
  • AzureStorage – файловое хранилище располагается в Microsoft Azure, загрузка файлов производится в Azure Blob Storage (реализация в планах)
  • SSH – файловое хранилище располагается на удаленном сервере, загрузка файлов производится по SSH-протоколу (реализация в планах)

В планах также реализация интеграции с хранилищем Amazon S3.

Послесловие


Хочу выразить благодарность gelas за помощь в работе над проектом и здравую критику.

Отдельно хочу отметить, что проект создавался мною в рамках комьюнити украинских разработчиков .NET Core — .NET Core Ukrainian User Group. На данный момент наибольшая активность участников наблюдается в группе в Facebook, но и группу в ВК мы также планируем наполнять. Если у вас есть вопросы по .NET Core, идеи которыми вы хотите поделиться, или вы только присматриваетесь к этой технологии – присоединяйтесь, будет интересно!

Проект доступен под лицензией MIT на GitHub.



1. Руководство по ASP.NET Dynamic Data.
2. Википедия. Скаффолдинг.
Поделиться с друзьями
-->

Комментарии (11)


  1. Flaksirus
    10.10.2016 13:42

    Упаковать бы это в nuget пакет и сделать каталог examples с примерами, так можно жить первое время без документации


    1. Ernado
      10.10.2016 13:43

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


      1. Flaksirus
        10.10.2016 14:17
        +1

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


        1. Ernado
          10.10.2016 15:36

          Постараюсь в ближайшее время сделать примеры! Если будет желание — присоединяйтесь к разработке :)


          1. kanekt
            11.10.2016 09:40

            на Angular2 для front-end не планируете переходить?

            Когда будет время — надо будет посмотреть проект и может что реализовать :)


            1. Ernado
              11.10.2016 10:45

              Есть очень большое желание попробовать Angular 2, только к сожалению, времени на это нет :( Базовую версию редактора я сделал на первой версии Angular, а сейчас вместе с gelas разрабатываем более продвинутый UI на react: https://github.com/dncuug/scaffolder-ui


              1. kanekt
                11.10.2016 10:49

                то есть можно не ждать Angular2 :) если уж на react пересели.


                1. Ernado
                  11.10.2016 12:33

                  Скорее всего, что нет. Но это не точно :) На самом деле UI может быть написан на чем угодно — это может быть даже мобильное, или декстопное приложение. REST API позволяет реализовать такую интеграцию. Так что если есть необходимость интеграции с приложением на AngularJS 2 — то вы можете взять только серверное ядро и дальше использовать его со своим интерфейсом.


                  1. kanekt
                    11.10.2016 17:05

                    Можно взять ядро, но тогда придется UI заново писать на Angular2. Вряд ли на это будет время при коммерческой разработке.


  1. kanekt
    11.10.2016 09:31

    Основной кейс использования админки для работы с данными/ CRM?
    Пока не совсем могу придумать, где можно было использовать данный пакет…


    1. Ernado
      11.10.2016 10:42

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