Найти в Дзене

Изменение Schema в Revit без потери данных

Оглавление
Когда уже умеешь программировать, то рассматриваешь всё только с позиции микроскопа. но иногда молоток тоже вполне себе решение
(с) Конфуций, V в. до н.э.

Всем привет! Сегодня расскажу про свою библиотеку для генерации кода, которая упрощает работу со схемами в Revit API, а так же позволяет их изменять. Да и вообще, упрощает работу со схемами почти до уровня обычного запроса к базе данных.

Введение

  • Итак, допустим, ваш плагин должен хранить какую-то информацию в недоступном для пользователя виде. Очевидный вариант — внешнее хранилище ExtensibleStorage. Оно действительно позволяет хранить много информации, но имеет минусы:
  • Ограниченный набор типов данных
  • С ним неудобно работать
  • Однажды созданную схему поменять нельзя

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

Работа с базами данных в Entity Framework

Отойдём чуть-чуть в сторону. Кратко расскажу, как работает Entity Framework — система для работы с базами данных для веб-приложений на C#, позволяющая использовать LINQ вместо прямых запросов SQL.

1. Мы определяем класс Модели — сущность, которая будет храниться в таблице базы данных. Одна модель — одна таблица, а каждое свойство модели — столбец таблицы.

2. Добавляем нашу модель в качестве DbSet<Model> в класс-наследник класса DbContext — основной класс для доступа к базе данных.

3. Пишем в терминале команду 'dotnet ef migration add ...'.

4. Entity Framework генерирует нам файл с кодом на C# (миграцию), который показывает, как наша база данных изменилась. Какие таблицы добавились, какие исчезли и как модифицировались столбцы

5. Пишем в терминале 'dotnet ef update database', и миграция применяется к базе данных. Все таблицы обновляются.

6. Если мы изменили или добавили модель, то повторяем шаги 3-5. Таблицы в базе данных меняются, существовавшая в них информация при этом никуда не исчезает.

Собственно, для чего это всё. Я создал библиотеку, которая делает всё тоже самое, но только для Revit Extensible Storage.

Библиотека Atomatiq.SchemaMigrations

Код библиотеки находится тут: https://github.com/atomatiq/SchemaMigrations

А тут — пошаговая инструкция по работе.

Итак, как теперь мы решаем проблему со схемами:

  1. Подключаем к проекту библиотеку Atomatiq.SchemaMigrations.Database
  2. Определяем класс модели. Каждое свойство модели — будущее поле в схеме, поэтому они должны быть только совместимого типа

3. Создаём класс наследник класса SchemaContext, определяем в нем набор из SchemaSet<Model>:

-2

4. Устанавливаемый глобально тул для генерации миграций

5. Запускаем в терминале 'schema-migration-add InitialMigration'.

6. Генератор миграций создаёт нам файл миграции:

-3

Да, это всё автоматически сгенерированный код. Вот так, кстати, выглядит код, который генерирует другой код:

-4

6. Теперь нам надо воспользоваться тем, что мы нагенерировали. Все миграции применятся автоматически, как только мы обратимся к любому элементу. Если мы как-то меняли схему, а в проекте осталась старая версия, то все данные перенесутся в новосозданную схему, а из старой — удалятся. Чтобы воспользоваться схемой, используем класс DatabaseConnection<TModel>. В качестве параметра при создании он принимает элемент, в котором будет хранится Entity данной схемы.

-5

Для работы со схемой нужны 2 метода:

  • SaveObject сохраняет в схему данные, которые мы передали в виде экземпляра класса Модели (указанной в угловых скобках у DatabaseConnection<Model>).
  • LoadObject считывает данные их схемы и возвращает их в виде экземпляра класса модели. Если в Entity не существует, то вернётся пустой объект. Существование объекта в схеме проверяет метод HasObject.

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

Пример клиентского приложения, использующего данную библиотеку:

GitHub - SergeyNefyodov/SchemaMigrationsExample

Обращение к читателям

Во-первых, я буду рад, если вы поддержите репозиторий проекта звездой на гитхабе.

Во-вторых, я буду невероятно рад, если вы воспользуетесь этой библиотекой на своём рабочем проекте. В качестве основной инструкции используйте readme.md.

В-третьих, в проекте могут быть баги, а в readme.md — непонятки. Если вы найдёте баг — смело создавайте Issue в репозитории. А также можете вложить и свой вклад в проект, исправив баг и сделав пулл-реквест, или предложив упрощение для readme.

Заключение

Конечно же, не забывайте подписываться на мой телеграм-канал о Revit API и на меня лично в GitHub. Это моя первая публичная библиотека, так что не судите строго и делитесь своими мыслями в комментариях. До новых встреч!

-6