Найти в Дзене
IT's BIM | Revit и C#

Настраиваем мультиверсионность для Revit API. Как писать под любую версию Revit.

В прошлом посте мы разобрали агрегатор для независимых плагинов. Теперь — как собирать их под все версии Revit (2019–2027) без копипасты проектов. Особенно больно нам становится при переходе с .NET 4.8 (Revit 2024) на .NET 8 (Revit 2025). Решение — Directory.Build.props. Файл, который MSBuild автоматически подхватывает для всех проектов. В него выносим: Результат: .csproj остаётся чистым, а версионность управляется в одном месте. Создайте файл Directory.Build.props в корне решения: Давайте разберем, что тут написано. Вот тут мы указываем, какие конфигурации у нас будут присутствовать для каждого проекта. То есть, тут мы задали, что любой проект будет обладать конфигурациями R2024, R2025 и R2026 (естественно, для полноценной разработки тут нужно дописать R2019-R2023, так же через точку с запятой) Вот тут происходит магия выбора фреймворка. Тут я указываю, что когда выбрана конфигурация R2024 (первая строка), то в качестве фреймворка я выбираю net48 (это .NET Framework 4.8). И, как несло
Оглавление

Введение

В прошлом посте мы разобрали агрегатор для независимых плагинов. Теперь — как собирать их под все версии Revit (2019–2027) без копипасты проектов.

Особенно больно нам становится при переходе с .NET 4.8 (Revit 2024) на .NET 8 (Revit 2025). Решение — Directory.Build.props.

Что такое Directory.Build.props🔧

Файл, который MSBuild автоматически подхватывает для всех проектов. В него выносим:

  • Конфигурации (R2019…R2027)
  • TargetFramework под каждую версию
  • OutputPath и константы для #if REVIT_2025
  • Общие свойства: UseWPF, PlatformTarget=x64

Результат: .csproj остаётся чистым, а версионность управляется в одном месте.

Настройка и разбор Directory.Build.props ⚙️

Создайте файл Directory.Build.props в корне решения:

Примерное содержание файла Directory.Build.props
Примерное содержание файла Directory.Build.props

Давайте разберем, что тут написано.

Конфигурации проектов
Конфигурации проектов

Вот тут мы указываем, какие конфигурации у нас будут присутствовать для каждого проекта. То есть, тут мы задали, что любой проект будет обладать конфигурациями R2024, R2025 и R2026 (естественно, для полноценной разработки тут нужно дописать R2019-R2023, так же через точку с запятой)

RevitVersion и TargetFramework
RevitVersion и TargetFramework

Вот тут происходит магия выбора фреймворка. Тут я указываю, что когда выбрана конфигурация R2024 (первая строка), то в качестве фреймворка я выбираю net48 (это .NET Framework 4.8). И, как несложно догадаться, для R2025 и R2026 мы вместо net48 указываем net8.0-windows.

OutputPath и DefineConstants
OutputPath и DefineConstants

Вот здесь мы задаем выходной путь (куда будет по итогу собираться/билдиться) нашему проекту и определяем константы для условной компиляции.

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

Условная компиляция позволяет нам адаптировать наш проект под любую версию Revit. Давайте разберем на примере. В старых версиях Revit (2019-2021) единицы измерения управлялись через Enum под названием DisplayUnitType. Начиная с Revit 2022 этот Enum был заменен на статический класс под названием UnitTypeId. И как нам, не переписывая проект, сделать так, чтобы при выборе старой (R2019-R2021) конфигурации , мы работали с DisplayUnitType, а при выборе новой (любой другой) конфигурации — с UnitTypeId? Именно тут нам на помощь приходят DefineConstants. Мы объявили, что для условной компиляции мы можем использовать:

  • DEBUG — по умолчанию было, можно убрать за ненадобностью
  • $(Configuration) — конфигурация проекта
  • REVIT_$(RevitVersion) — Версия Revit

Это значит, что в коде я могу написать следующее:

Пример условной компиляции
Пример условной компиляции

Тут указано, что если выбрана версия R2019 || R2020 || R2021, то я буду делать entity.Set при помощи DisplayUnitType:

Я выбрал версию R2021 и теперь работаю с верхним условием, а нижний блок от #else до #endif будет проигнорирован
Я выбрал версию R2021 и теперь работаю с верхним условием, а нижний блок от #else до #endif будет проигнорирован

И теперь вишенка на торте. Как писать код, не имея нужной версии Revit? Я хочу написать плагин или проверить совместимость своего плагина с версией Revit 2026, которой нет на компьютере. И делать я это буду при помощи NuGet-пакета, который так же уже указан в Directory.Build.props:

Пакет для написания плагинов под Revit, содержит в себе RevitAPI.dll, RevitAPIUI.dll и другие, необходимые для полноценной разработки
Пакет для написания плагинов под Revit, содержит в себе RevitAPI.dll, RevitAPIUI.dll и другие, необходимые для полноценной разработки

Как вы можете заметить, версия пакета управляется через RevitVersion. То есть, при смене конфигурации вы автоматически будете переключаться между .dll-файлами Revit, при этом, все эти файлы содержатся внутри пакета, поэтому вам даже Revit не нужен для того, чтобы писать плагины под него😁

Настройка файла .csproj

Что же до файла .csproj — в нем остается только необходимый ему минимум. Вы можете прямо сейчас открыть свой .csproj файл и посмотреть, какой он огромный. А вот так они выглядят у меня:

Новый .csproj
Новый .csproj

По сути, мне здесь остается только сказать, что это Library (библиотека классов) и все. Ссылки добавляются в ходе написания кода, и бывают проекты, которым они не нужны. Тогда их .csproj файл вообще состоит только из этих 5 строк:

Минималистичный .csproj файл
Минималистичный .csproj файл

Настройка IDE

После предыдущих шагов вам осталось только создать конфигурации решения в IDE.

Делается это через Build → Configuration Manager → <New…>, там вы создаете конфигурации под все версии Revit (R2019 → R2026).

В целом, это все. Вне зависимости от того, используете ли вы агрегатор из прошлого поста, или не используете, вы можете повторить данный алгоритм на своем решении и получить тот самый, ожидаемый результат. Но вот простая диаграмма, которая еще раз закрепит понимание, как работает Directory.Build.props с проектом-агрегатором:

Directory.Build.props в проекте-агрегаторе
Directory.Build.props в проекте-агрегаторе

Он просто передает настройки, определенные в нем, всем проектам, которые есть в решении MainApp.

Готовый файл 📦

Полный Directory.Build.props с поддержкой 2019–2027 — в моем телеграм-канале. Просто положите его в корень и создайте конфигурации решения, и будет вам счастье.

--

Мучились ли вы с версиями фреймворка? Как делали мультиверсионность? Как работали с .dll-ками Revit? Остались вопросы? Все это можно рассказать в комментариях!