diff --git a/SQL-Server-SQLCLR/Projects/DevAdmHelpers/Readme.md b/SQL-Server-SQLCLR/Projects/DevAdmHelpers/Readme.md
index ce51faf..a95c9de 100644
--- a/SQL-Server-SQLCLR/Projects/DevAdmHelpers/Readme.md
+++ b/SQL-Server-SQLCLR/Projects/DevAdmHelpers/Readme.md
@@ -2,6 +2,10 @@
Расширение SQLCLR для SQL Server с различными функциями для разработчиков и администраторов.
+## Собранное решение
+
+Собранную DLL для установки расширения SQLCLR можно скачать в разделе [релизы](https://github.com/YPermitin/SQLServerTools/releases).
+
## Обратная связь и новости
Вопросы, предложения и любую другую информацию [отправляйте на электронную почту](mailto:i.need.ypermitin@yandex.ru).
@@ -60,10 +64,15 @@
1. Собрать проект **DevAdmHelpers** в режиме **Release**.
2. Полученную DLL **DevAdmHelpers.dll** скопировать на сервер, где установлен экземпляр SQL Server. Пусть для примера путь к DLL на сервере будет **"C:\Share\SQLCLR\DevAdmHelpers.dll"**.
3. Выбрать базу для установки. Например, пусть она называется **SQLServerMaintenance**.
-4. Для упрощения настройки опустим некоторые аспекты безопасности и разрешим установку неподписанных расширений.
+
+4. [Включим интеграцию с CLR](https://learn.microsoft.com/en-us/sql/relational-databases/clr-integration/clr-integration-enabling?view=sql-server-ver16). Для упрощения настройки опустим некоторые аспекты безопасности и разрешим установку неподписанных расширений.
```sql
+EXEC sp_configure 'clr enabled', 1;
+RECONFIGURE;
+GO
ALTER DATABASE SQLServerMaintenance SET TRUSTWORTHY ON;
+GO
```
Также разрешим текущему пользователю доступ к внешним ресурсам. Например, это пользователь **YY\ypermitin**.
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/Readme.md b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/Readme.md
index 88e4050..0164a0b 100644
--- a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/Readme.md
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/Readme.md
@@ -1,3 +1,290 @@
# YellowMetadataReader
-В разработке...
\ No newline at end of file
+Расширение SQLCLR для SQL Server с различными функциями для работы с информационными базами платформы 1С:Предприятие 8.x+. Базовый функционал расширения SQLCLR позволяет:
+
+* Получить список баз 1С на SQL Server.
+* Получить список таблиц базы 1С с расшифровкой имен в терминах прикладного решения платформы 1С.
+* Получить список таблиц базы 1С с полями с расшифровкой имен в терминах прикладного решения платформы 1С.
+* Получить список перечислений информационной базы 1С с расшифровкой их значений и порядка.
+* Расшифровать внутренний формат данных 1С, хранящийся в двоичных данных, в читаемую (на сколько это возможно) строку.
+
+Подробнее о возможностях и установки SQLCLR-расширения ниже.
+
+## Собранное решение
+
+Собранную DLL для установки расширения SQLCLR можно скачать в разделе [релизы](https://github.com/YPermitin/SQLServerTools/releases).
+
+## Обратная связь и новости
+
+Вопросы, предложения и любую другую информацию [отправляйте на электронную почту](mailto:i.need.ypermitin@yandex.ru).
+
+Новости по проектам или новым материалам в [Telegram-канале](https://t.me/TinyDevVault).
+
+## Благодарности
+
+SQLCLR расширение **YellowMetadataReader** базируется на решении [Жичкина Дмитрия](https://github.com/zhichkin) - [dajet-metadata](https://github.com/zhichkin/dajet-metadata), которое изначально создавалось для чтения метаданных платформы 1С:Предприятие 8 напрямую из базы данных. Исходная версия поддерживает работу с SQL Server и PostgreSQL.
+
+Выражаю огромную благодарность автору за его труды под [открытой лицензией](https://github.com/zhichkin/dajet-metadata/blob/main/LICENSE) и несгибаемую волю в намерении раскопать все что можно по работе "желтой" платформы.
+
+Вопросы автору DeJet можете задать в [Telegram-канале](https://t.me/dajet_studio).
+
+### Отличие от исходной разработки
+
+Решение [dajet-metadata](https://github.com/zhichkin/dajet-metadata) было адаптировано под более узкие задачи:
+
+* Переведено на платформу .NET Framework 4.8, т.к. SQLCLR базируется на работе именно этой среды выполнения CLR.
+* Убрана поддержка PostgreSQL.
+* Добавлено чтение некоторых служебных таблиц платформы 1С и распознавание их свойств (например, таблицы итогов регистров накопления, некоторые доп. поля для перечислений и констант и др.).
+
+При этом сохранена в целом архитектура при работе с метаданными платформы 1С.
+
+## Функциональность
+
+В текущей версии добавлен модуль **EntryMetadata** со следующими функциями.
+
+### GetInfobases
+
+Получения списка баз, которые относятся к базам платформы 1С. Для каждой базы отображается информация о конфигурации и дате последнего обновления информационной базы из конфигуратора. Пример вывода функции для одной из баз 1С на сервере.
+
+```sql
+SELECT * FROM [dbo].[fn_GetInfobases]()
+```
+
+| InfobaseName | ConfigVersion | ConfigAlias | ConfigName | ConfigUiCompatibilityMode | PlatformVersion | InfobaseLastUpdate |
+|--------------|---------------|--------------------------------------------------------------------------------|------------------------------------|---------------------------|-----------------|-------------------------|
+| BSL-ORIG | 3.1.7.34 | Демонстрационная конфигурация "Библиотека стандартных подсистем", редакция 3.1 | БиблиотекаСтандартныхПодсистемДемо | TaxiAllowVersion82 | 80314 | 2022-06-13 19:09:36.000 |
+
+При этом базы, не относящиеся к платформе 1С, в списке не отображаются. Может использоваться для поиска в скриптах баз 1С по имени конфигурации или версии, например, в скриптах обслуживания.
+
+### GetInfobaseTables
+
+Получение списка таблиц информационной базы в терминах прикладного решения. Позволяет получить список таблиц информционной базы 1С с маппингом имени таблицы SQL с именем таблицы в терминах прикладного решения. Пример вывода ниже.
+
+```sql
+SELECT * FROM [dbo].[fn_GetInfobaseTables]('BSL-ORIG')
+```
+
+| TableSQL | Table1C |
+|--------------|------------------------------------------------------|
+| _Acc3930 | ПланСчетов._ДемоОсновной |
+| _AccRg3942 | РегистрБухгалтерии._ДемоОсновной |
+| _AccumRg505 | РегистрНакопления._ДемоОстаткиТоваровВМестахХранения |
+| _AccumRg1265 | РегистрНакопления._ДемоОборотыПоСчетамНаОплату |
+| _Reference12 | Справочник._ДемоВидыНоменклатуры |
+
+Может использоваться как для скриптов обслуживания, так и для анализа базы данных в понятных терминах прикладного решения (например, занятого места по таблицам и так далее).
+
+### InfobaseTablesWithFields
+
+Получение списка таблиц с полями информационной базы в терминах прикладного решения. Позволяет получить список таблиц информционной базы 1С с маппингом имени таблицы SQL с именем таблицы в терминах прикладного решения, при этом для каждой таблицы выводится список полей в терминах SQL-базы и прикладного решения. Пример вывода ниже.
+
+```sql
+SELECT * FROM [dbo].[fn_GetInfobaseTablesWithFields]('BSL-ORIG')
+```
+
+| TableSQL | Table1C | FieldSQL | Field1C |
+|----------|--------------------------|---------------|------------------|
+| _Acc3930 | ПланСчетов._ДемоОсновной | _IDRRef | Ссылка |
+| _Acc3930 | ПланСчетов._ДемоОсновной | _Version | ВерсияДанных |
+| _Acc3930 | ПланСчетов._ДемоОсновной | _Marked | ПометкаУдаления |
+| _Acc3930 | ПланСчетов._ДемоОсновной | _PredefinedID | Предопределённый |
+| _Acc3930 | ПланСчетов._ДемоОсновной | _ParentIDRRef | Родитель |
+| _Acc3930 | ПланСчетов._ДемоОсновной | _Code | Код |
+
+Может использоваться как для скриптов обслуживания, так и для анализа базы данных в понятных терминах прикладного решения (например, занятого места по таблицам и так далее).
+
+### GetInfobasesEnumerations
+
+Получения списка перечислений информационной базы с их значениями в терминах прикладного решения. По умолчанию названия значений перечислений в базе данных в явном виде не хранятся. Данный методв позволяет получить к этой информации доступ без использования самой платформы 1С. Пример вывода ниже.
+
+```sql
+SELECT * FROM dbo.[fn_GetInfobasesEnumerations]('BSL-ORIG')
+ORDER BY Enumeration, ValueOrder
+```
+
+| TableSQL | Enumeration | ValueId | ValueName | ValueAlias | ValueOrder |
+|-----------|---------------------------------------------|--------------------------------------|-----------|--------------------------------------|------------|
+| _Enum3932 | Перечисление._ДемоВидыПлатежейВБюджет | a03d3535-090a-46f5-a411-3567c8c12bc1 | Налог | Налог (взносы): начислено / уплачено | 0 |
+| _Enum5120 | Перечисление._ДемоПолФизическогоЛица | 5545798f-72dc-4630-a5ba-88039f4bfe3c | Мужской | Мужской | 0 |
+| _Enum5120 | Перечисление._ДемоПолФизическогоЛица | f293ac4b-91ed-4c97-90b3-bfe98c983a0f | Женский | Женский | 1 |
+| _Enum5406 | Перечисление._ДемоСовместимостьНоменклатуры | b1d3d9e3-c33a-4a3c-acf5-9fa42c4f8718 | Полная | Полная | 0 |
+| _Enum5406 | Перечисление._ДемоСовместимостьНоменклатуры | 2f47632e-c9cf-4c99-ab40-37960b23b582 | Частичная | Частичная | 1 |
+
+Может использоваться как для скриптов обслуживания, так и для анализа базы данных в понятных терминах прикладного решения (например, занятого места по таблицам и так далее). При определенных ограничениях может использоваться в целях построения хранилищ даннных и построения интеграций.
+
+### InternalFormatData
+
+Сервисная функция для преобразования двоичных данных внутреннего формата платформы 1С к читаемым (на сколько это возможно) строкам. Например, плафторма 1С хранит множество системных данных в базе в виде собственного формата. Данная функция позволит их прочитать в какой-то внятной форме.
+
+```sql
+SELECT [FileName]
+ ,[SQLServerMaintenance].dbo.fn_ParseInternalString(BinaryData) AS [FileDataAsString]
+ FROM [BSL-ORIG].[dbo].[Params]
+ WHERE FileName = 'DBNames'
+```
+
+| FileName | FileDataAsString |
+| -------- | ---------------- |
+| DBNames | <очень длинная строка, фрагмент ниже> |
+
+```
+{9063,
+{5728,
+{00000000-0000-0000-0000-000000000000,"SystemSettings",1},
+{00000000-0000-0000-0000-000000000000,"CommonSettings",2},
+{00000000-0000-0000-0000-000000000000,"RepSettings",3},
+{00000000-0000-0000-0000-000000000000,"RepVarSettings",4},
+...продолжение следует... |
+```
+
+В основном используется внутри компоненты SQLCLR для разбора метаданных конфигурации, но можно использовать и вручную для диагностики работы сложных моментов.
+
+## Окружение для разработки
+
+Для окружение разработчика необходимы:
+
+* [.NET Framework 4.8 SDK](https://support.microsoft.com/ru-ru/topic/microsoft-net-framework-4-8-автономный-установщик-для-windows-9d23f658-3b97-68ab-d013-aa3c3e7495e0)
+* [.NET 6 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)
+* [Visual Studio 2022](https://visualstudio.microsoft.com/ru/vs/)
+* [Microsoft SQL Server 2012+](https://www.microsoft.com/ru-ru/sql-server/sql-server-downloads)
+* [Плафторма 1С 8.2 и новее](https://v8.1c.ru/platforma/).
+
+## Состав проекта
+
+Проекты и библиотеки в составе решения:
+
+* **Apps** - различные приложения
+
+ * **YellowMetadataReader.CLI** - пример приложения для работы с библиотекой.
+
+* **Libs** - библиотеки и вспомогательные проекты.
+
+ * **YellowMetadataReader** - проект библиотеки для расширения SQLCLR.
+
+## Установка
+
+Для установки необходимо выполнить несколько шагов:
+
+1. Собрать проект **YellowMetadataReader** в режиме **Release**.
+2. Полученную DLL **YellowMetadataReader.dll** скопировать на сервер, где установлен экземпляр SQL Server. Пусть для примера путь к DLL на сервере будет **"C:\Share\SQLCLR\YellowMetadataReader.dll"**.
+3. Выбрать базу для установки. Например, пусть она называется **SQLServerMaintenance**.
+4. [Включим интеграцию с CLR](https://learn.microsoft.com/en-us/sql/relational-databases/clr-integration/clr-integration-enabling?view=sql-server-ver16). Для упрощения настройки опустим некоторые аспекты безопасности и разрешим установку неподписанных расширений.
+
+```sql
+EXEC sp_configure 'clr enabled', 1;
+RECONFIGURE;
+GO
+ALTER DATABASE SQLServerMaintenance SET TRUSTWORTHY ON;
+GO
+```
+
+Также разрешим текущему пользователю доступ к внешним ресурсам. Например, это пользователь **YY\ypermitin**.
+
+```sql
+use [master]; GRANT EXTERNAL ACCESS ASSEMBLY TO [YY\ypermitin];
+```
+
+5. Если ранее расширение SQLCLR устанавливалось, то удалим все зарегистрированные функции и саму сборку перед повторной установкой.
+
+```sql
+DROP FUNCTION IF EXISTS [dbo].[fn_GetInfobases];
+DROP FUNCTION IF EXISTS [dbo].[fn_GetInfobaseTables];
+DROP FUNCTION IF EXISTS [dbo].[fn_GetInfobaseTablesWithFields];
+DROP FUNCTION IF EXISTS [dbo].[fn_ParseInternalString];
+DROP FUNCTION IF EXISTS [dbo].[fn_GetInfobasesEnumerations];
+DROP ASSEMBLY IF EXISTS [YPermitin.SQLCLR.YellowMetadataReader];
+```
+
+6. Далее добавим сборку SQLCLR в базу **SQLServerMaintenance**.
+
+```sql
+USE [SQLServerMaintenance]
+GO
+
+CREATE ASSEMBLY [YPermitin.SQLCLR.YellowMetadataReader]
+ FROM 'C:\Share\SQLCLR\YellowMetadataReader.dll'
+ WITH PERMISSION_SET = UNSAFE;
+GO
+```
+
+6. Теперь добавим все функции расширения, которые были описаны выше.
+
+```sql
+CREATE FUNCTION [dbo].[fn_GetInfobases]()
+RETURNS TABLE (
+ InfobaseName nvarchar(255),
+ ConfigVersion nvarchar(250),
+ ConfigAlias nvarchar(250),
+ ConfigName nvarchar(250),
+ ConfigUiCompatibilityMode nvarchar(50),
+ PlatformVersion nvarchar(50),
+ InfobaseLastUpdate datetime null
+)
+AS
+EXTERNAL NAME [YPermitin.SQLCLR.YellowMetadataReader].[YPermitin.SQLCLR.YellowMetadataReader.EntryMetadata].[GetInfobases];
+GO
+
+CREATE FUNCTION [dbo].[fn_GetInfobaseTables](@databaseName nvarchar(255))
+RETURNS TABLE (
+ TableSQL nvarchar(512),
+ Table1C nvarchar(512)
+)
+AS
+EXTERNAL NAME [YPermitin.SQLCLR.YellowMetadataReader].[YPermitin.SQLCLR.YellowMetadataReader.EntryMetadata].[GetInfobaseTables];
+GO
+
+CREATE FUNCTION [dbo].[fn_GetInfobaseTablesWithFields](@databaseName nvarchar(255))
+RETURNS TABLE (
+ TableSQL nvarchar(512),
+ Table1C nvarchar(512),
+ FieldSQL nvarchar(512),
+ Field1C nvarchar(512)
+)
+AS
+EXTERNAL NAME [YPermitin.SQLCLR.YellowMetadataReader].[YPermitin.SQLCLR.YellowMetadataReader.EntryMetadata].[GetInfobaseTablesWithFields];
+GO
+
+CREATE FUNCTION [dbo].[fn_GetInfobasesEnumerations](@databaseName nvarchar(255))
+RETURNS TABLE (
+ TableSQL nvarchar(512),
+ Enumeration nvarchar(512),
+ ValueId nvarchar(512),
+ ValueName nvarchar(512),
+ ValueAlias nvarchar(512),
+ ValueOrder int
+)
+AS
+EXTERNAL NAME [YPermitin.SQLCLR.YellowMetadataReader].[YPermitin.SQLCLR.YellowMetadataReader.EntryMetadata].[GetInfobasesEnumerations];
+GO
+
+CREATE FUNCTION [dbo].[fn_ParseInternalString](@data [varbinary](max))
+RETURNS nvarchar(max) WITH EXECUTE AS CALLER
+AS
+EXTERNAL NAME [YPermitin.SQLCLR.YellowMetadataReader].[YPermitin.SQLCLR.YellowMetadataReader.EntryMetadata].[ParseInternalString];
+GO
+```
+
+7. Все готово для использования.
+
+```sql
+USE [SQLServerMaintenance]
+GO
+
+SELECT * FROM [dbo].[fn_GetInfobases] ()
+GO
+
+-- BSL-ORIG - имя тестовой баз данных.
+SELECT * FROM [dbo].[fn_GetInfobaseTables]('BSL-ORIG')
+GO
+
+SELECT * FROM [dbo].[fn_GetInfobaseTablesWithFields]('BSL-ORIG')
+GO
+
+SELECT * FROM dbo.[fn_GetInfobasesEnumerations]('BSL-ORIG')
+ORDER BY Enumeration, ValueOrder
+GO
+```
+
+Подробнее о настройках и разработке расширений SQLCLR можно найти материалы [здесь](../../).
+
+
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.CLI/App.config b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.CLI/App.config
new file mode 100644
index 0000000..193aecc
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.CLI/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.CLI/Program.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.CLI/Program.cs
new file mode 100644
index 0000000..4dd6100
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.CLI/Program.cs
@@ -0,0 +1,24 @@
+using YPermitin.SQLCLR.YellowMetadataReader;
+
+namespace YellowMetadataReader.CLI
+{
+ internal class Program
+ {
+ static void Main()
+ {
+ EntryBase.ConnectionString = "server=localhost;database=master;trusted_connection=true;";
+
+ var infobases = EntryMetadata.GetInfobases();
+ foreach (var infobase in infobases)
+ {
+ EntryMetadata.GetInfobasesFillRow(infobase,
+ out _,
+ out _,
+ out _,
+ out _,
+ out _,
+ out _, out _);
+ }
+ }
+ }
+}
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.CLI/Properties/AssemblyInfo.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.CLI/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..ada7478
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.CLI/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// Общие сведения об этой сборке предоставляются следующим набором
+// набора атрибутов. Измените значения этих атрибутов для изменения сведений,
+// связанные с этой сборкой.
+[assembly: AssemblyTitle("YellowMetadataReader.CLI")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("YellowMetadataReader.CLI")]
+[assembly: AssemblyCopyright("Copyright © 2023")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Установка значения False для параметра ComVisible делает типы в этой сборке невидимыми
+// для компонентов COM. Если необходимо обратиться к типу в этой сборке через
+// из модели COM задайте для атрибута ComVisible этого типа значение true.
+[assembly: ComVisible(false)]
+
+// Следующий GUID представляет идентификатор typelib, если этот проект доступен из модели COM
+[assembly: Guid("27c4a27c-9ec9-4c96-aeee-9aa2a6e26a7e")]
+
+// Сведения о версии сборки состоят из указанных ниже четырех значений:
+//
+// Основной номер версии
+// Дополнительный номер версии
+// Номер сборки
+// Номер редакции
+//
+// Можно задать все значения или принять номера сборки и редакции по умолчанию
+// используя "*", как показано ниже:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.CLI/YellowMetadataReader.CLI.csproj b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.CLI/YellowMetadataReader.CLI.csproj
new file mode 100644
index 0000000..652e263
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.CLI/YellowMetadataReader.CLI.csproj
@@ -0,0 +1,59 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {27C4A27C-9EC9-4C96-AEEE-9AA2A6E26A7E}
+ Exe
+ YellowMetadataReader.CLI
+ YellowMetadataReader.CLI
+ v4.8
+ 512
+ true
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {c4503533-bfbd-4e7e-a00e-941746748ddd}
+ YellowMetadataReader
+
+
+
+
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.sln b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.sln
new file mode 100644
index 0000000..2038309
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.sln
@@ -0,0 +1,44 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.33530.505
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YellowMetadataReader", "YellowMetadataReader\YellowMetadataReader.csproj", "{C4503533-BFBD-4E7E-A00E-941746748DDD}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libs", "Libs", "{2489FF06-C047-4ACE-A97B-6FE70C906F0B}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{6CF64E2A-0BDA-4E45-BAB0-924BEBCE20B1}"
+ ProjectSection(SolutionItems) = preProject
+ Readme.md = Readme.md
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Apps", "Apps", "{04251AC8-3B7D-4FEC-963B-C3A3FBE63FE6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YellowMetadataReader.CLI", "YellowMetadataReader.CLI\YellowMetadataReader.CLI.csproj", "{27C4A27C-9EC9-4C96-AEEE-9AA2A6E26A7E}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {C4503533-BFBD-4E7E-A00E-941746748DDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C4503533-BFBD-4E7E-A00E-941746748DDD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C4503533-BFBD-4E7E-A00E-941746748DDD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C4503533-BFBD-4E7E-A00E-941746748DDD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {27C4A27C-9EC9-4C96-AEEE-9AA2A6E26A7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {27C4A27C-9EC9-4C96-AEEE-9AA2A6E26A7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {27C4A27C-9EC9-4C96-AEEE-9AA2A6E26A7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {27C4A27C-9EC9-4C96-AEEE-9AA2A6E26A7E}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {C4503533-BFBD-4E7E-A00E-941746748DDD} = {2489FF06-C047-4ACE-A97B-6FE70C906F0B}
+ {27C4A27C-9EC9-4C96-AEEE-9AA2A6E26A7E} = {04251AC8-3B7D-4FEC-963B-C3A3FBE63FE6}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {21E37607-AD9A-4719-B74C-B40CB809F021}
+ EndGlobalSection
+EndGlobal
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.sln.DotSettings b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.sln.DotSettings
new file mode 100644
index 0000000..32e7a98
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.sln.DotSettings
@@ -0,0 +1,9 @@
+
+ SQL
+ UTF
+ True
+ True
+ True
+ True
+ True
+ True
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/AccountEnricher.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/AccountEnricher.cs
new file mode 100644
index 0000000..bd09367
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/AccountEnricher.cs
@@ -0,0 +1,84 @@
+using System;
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects;
+using YPermitin.SQLCLR.YellowMetadataReader.Services;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Enrichers
+{
+ public sealed class AccountEnricher : IContentEnricher
+ {
+ private Configurator Configurator { get; }
+ public AccountEnricher(Configurator configurator)
+ {
+ Configurator = configurator;
+ }
+ public void Enrich(MetadataObject metadataObject)
+ {
+ if (!(metadataObject is Account account)) throw new ArgumentOutOfRangeException();
+
+ ConfigObject configObject = Configurator.FileReader.ReadConfigObject(account.FileName.ToString());
+
+ account.Uuid = configObject.GetUuid(new[] { 1, 3 });
+ account.Name = configObject.GetString(new[] { 1, 15, 1, 2 });
+ ConfigObject alias = configObject.GetObject(new[] { 1, 15, 1, 3 });
+ if (alias.Values.Count == 3)
+ {
+ account.Alias = configObject.GetString(new[] { 1, 15, 1, 3, 2 });
+ }
+ account.CodeType = CodeType.String;
+ account.CodeLength = configObject.GetInt32(new[] { 1, 22 });
+ account.DescriptionLength = configObject.GetInt32(new[] { 1, 23 });
+
+ Configurator.ConfigurePropertyСсылка(account);
+ Configurator.ConfigurePropertyВерсияДанных(account);
+ Configurator.ConfigurePropertyПометкаУдаления(account);
+ Configurator.ConfigurePropertyПредопределённый(account);
+ Configurator.ConfigurePropertyРодитель(account);
+
+ if (account.CodeLength > 0)
+ {
+ Configurator.ConfigurePropertyКод(account);
+ }
+ if (account.DescriptionLength > 0)
+ {
+ Configurator.ConfigurePropertyНаименование(account);
+ }
+
+ Configurator.ConfigurePropertyПорядок(account);
+ Configurator.ConfigurePropertyВид(account);
+ Configurator.ConfigurePropertyЗабалансовый(account);
+
+ // 6 - коллекция реквизитов плана счетов
+ ConfigObject properties = configObject.GetObject(new[] { 7 });
+ // 6.0 = 6e65cbf5-daa8-4d8d-bef8-59723f4e5777 - идентификатор коллекции реквизитов плана счетов
+ Guid propertiesUuid = configObject.GetUuid(new[] { 7, 0 });
+ if (propertiesUuid == new Guid("6e65cbf5-daa8-4d8d-bef8-59723f4e5777"))
+ {
+ Configurator.ConfigureProperties(account, properties, PropertyPurpose.Property);
+ }
+
+ Configurator.ConfigureSharedProperties(account);
+
+ // Признаки учета плана счетов
+ ConfigObject propertiesAccounting = configObject.GetObject(new[] { 8 });
+ // 6.0 = 78bd1243-c4df-46c3-8138-e147465cb9a4 - идентификатор коллекции реквизитов плана счетов
+ Guid propertiesUuidAccounting = configObject.GetUuid(new[] { 8, 0 });
+ if (propertiesUuidAccounting == new Guid("78bd1243-c4df-46c3-8138-e147465cb9a4"))
+ {
+ Configurator.ConfigureProperties(account, propertiesAccounting, PropertyPurpose.Property);
+ }
+
+ // 5 - коллекция табличных частей плана счетов
+ ConfigObject tableParts = configObject.GetObject(new[] { 5 });
+ // 5.0 = 932159f9-95b2-4e76-a8dd-8849fe5c5ded - идентификатор коллекции табличных частей плана счетов
+ Guid collectionUuid = configObject.GetUuid(new[] { 5, 0 });
+ if (collectionUuid == new Guid("4c7fec95-d1bd-4508-8a01-f1db090d9af8"))
+ {
+ Configurator.ConfigureTableParts(account, tableParts);
+ }
+
+ Configurator.ConfigurePredefinedValues(account);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/AccountingRegisterEnricher.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/AccountingRegisterEnricher.cs
new file mode 100644
index 0000000..b1c747b
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/AccountingRegisterEnricher.cs
@@ -0,0 +1,69 @@
+using System;
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects;
+using YPermitin.SQLCLR.YellowMetadataReader.Services;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Enrichers
+{
+ public sealed class AccountingRegisterEnricher : IContentEnricher
+ {
+ private Configurator Configurator { get; }
+ public AccountingRegisterEnricher(Configurator configurator)
+ {
+ Configurator = configurator;
+ }
+ public void Enrich(MetadataObject metadataObject)
+ {
+ if (!(metadataObject is AccountingRegister register)) throw new ArgumentOutOfRangeException();
+
+ ConfigObject configObject = Configurator.FileReader.ReadConfigObject(register.FileName.ToString());
+
+ register.Name = configObject.GetString(new[] { 1, 15, 1, 2 });
+ ConfigObject alias = configObject.GetObject(new[] { 1, 15, 1, 3 });
+ if (alias.Values.Count == 3)
+ {
+ register.Alias = configObject.GetString(new[] { 1, 15, 1, 3, 2 });
+ }
+
+ Configurator.ConfigurePropertyПериод(register);
+ Configurator.ConfigurePropertyНомерЗаписи(register);
+ Configurator.ConfigurePropertyАктивность(register);
+ Configurator.ConfigurePropertyСчетДт(register);
+ Configurator.ConfigurePropertyСчетКт(register);
+
+ // 7 - коллекция измерений
+ ConfigObject properties = configObject.GetObject(new[] { 3 });
+ // 7.0 = 35b63b9d-0adf-4625-a047-10ae874c19a3 - идентификатор коллекции измерений
+ Guid propertiesUuid = configObject.GetUuid(new[] { 3, 0 });
+ if (propertiesUuid == new Guid("35b63b9d-0adf-4625-a047-10ae874c19a3"))
+ {
+ Configurator.ConfigureProperties(register, properties, PropertyPurpose.Dimension);
+ }
+ // TODO: ???
+ // Configurator.ConfigurePropertyDimHash(register);
+ // Справка 1С: Хеш-функция измерений.
+ // Поле присутствует, если количество измерений не позволяет организовать уникальный индекс по измерениям.
+
+ // 5 - коллекция ресурсов
+ properties = configObject.GetObject(new[] { 5 });
+ // 5.0 = 63405499-7491-4ce3-ac72-43433cbe4112 - идентификатор коллекции ресурсов
+ propertiesUuid = configObject.GetUuid(new[] { 5, 0 });
+ if (propertiesUuid == new Guid("63405499-7491-4ce3-ac72-43433cbe4112"))
+ {
+ Configurator.ConfigureProperties(register, properties, PropertyPurpose.Measure);
+ }
+
+ // 6 - коллекция реквизитов
+ properties = configObject.GetObject(new[] { 7 });
+ // 6.0 = 9d28ee33-9c7e-4a1b-8f13-50aa9b36607b - идентификатор коллекции реквизитов
+ propertiesUuid = configObject.GetUuid(new[] { 6, 0 });
+ if (propertiesUuid == new Guid("9d28ee33-9c7e-4a1b-8f13-50aa9b36607b"))
+ {
+ Configurator.ConfigureProperties(register, properties, PropertyPurpose.Property);
+ }
+
+ Configurator.ConfigureSharedProperties(register);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/AccumulationRegisterEnricher.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/AccumulationRegisterEnricher.cs
new file mode 100644
index 0000000..ae228f9
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/AccumulationRegisterEnricher.cs
@@ -0,0 +1,73 @@
+using System;
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects;
+using YPermitin.SQLCLR.YellowMetadataReader.Services;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Enrichers
+{
+ public sealed class AccumulationRegisterEnricher : IContentEnricher
+ {
+ private Configurator Configurator { get; }
+ public AccumulationRegisterEnricher(Configurator configurator)
+ {
+ Configurator = configurator;
+ }
+ public void Enrich(MetadataObject metadataObject)
+ {
+ if (!(metadataObject is AccumulationRegister register)) throw new ArgumentOutOfRangeException();
+
+ ConfigObject configObject = Configurator.FileReader.ReadConfigObject(register.FileName.ToString());
+
+ register.Name = configObject.GetString(new[] { 1, 13, 1, 2 });
+ ConfigObject alias = configObject.GetObject(new[] { 1, 13, 1, 3 });
+ if (alias.Values.Count == 3)
+ {
+ register.Alias = configObject.GetString(new[] { 1, 13, 1, 3, 2 });
+ }
+ register.UseSplitter = configObject.GetInt32(new[] { 1, 20 }) == 1;
+ register.RegisterKind = (RegisterKind)configObject.GetInt32(new[] { 1, 15 });
+
+ Configurator.ConfigurePropertyПериод(register);
+ Configurator.ConfigurePropertyНомерЗаписи(register);
+ Configurator.ConfigurePropertyАктивность(register);
+ if (register.RegisterKind == RegisterKind.Balance)
+ {
+ Configurator.ConfigurePropertyВидДвижения(register);
+ }
+
+ // 7 - коллекция измерений
+ ConfigObject properties = configObject.GetObject(new[] { 7 });
+ // 7.0 = b64d9a43-1642-11d6-a3c7-0050bae0a776 - идентификатор коллекции измерений
+ Guid propertiesUuid = configObject.GetUuid(new[] { 7, 0 });
+ if (propertiesUuid == new Guid("b64d9a43-1642-11d6-a3c7-0050bae0a776"))
+ {
+ Configurator.ConfigureProperties(register, properties, PropertyPurpose.Dimension);
+ }
+ // TODO: ???
+ // Configurator.ConfigurePropertyDimHash(register);
+ // Справка 1С: Хеш-функция измерений.
+ // Поле присутствует, если количество измерений не позволяет организовать уникальный индекс по измерениям.
+
+ // 5 - коллекция ресурсов
+ properties = configObject.GetObject(new[] { 5 });
+ // 5.0 = b64d9a41-1642-11d6-a3c7-0050bae0a776 - идентификатор коллекции ресурсов
+ propertiesUuid = configObject.GetUuid(new[] { 5, 0 });
+ if (propertiesUuid == new Guid("b64d9a41-1642-11d6-a3c7-0050bae0a776"))
+ {
+ Configurator.ConfigureProperties(register, properties, PropertyPurpose.Measure);
+ }
+
+ // 6 - коллекция реквизитов
+ properties = configObject.GetObject(new[] { 6 });
+ // 6.0 = b64d9a42-1642-11d6-a3c7-0050bae0a776 - идентификатор коллекции реквизитов
+ propertiesUuid = configObject.GetUuid(new[] { 6, 0 });
+ if (propertiesUuid == new Guid("b64d9a42-1642-11d6-a3c7-0050bae0a776"))
+ {
+ Configurator.ConfigureProperties(register, properties, PropertyPurpose.Property);
+ }
+
+ Configurator.ConfigureSharedProperties(register);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/AccumulationRegisterTotalEnricher.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/AccumulationRegisterTotalEnricher.cs
new file mode 100644
index 0000000..0433b8c
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/AccumulationRegisterTotalEnricher.cs
@@ -0,0 +1,54 @@
+using System;
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects;
+using YPermitin.SQLCLR.YellowMetadataReader.Services;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Enrichers
+{
+ public sealed class AccumulationRegisterTotalEnricher : IContentEnricher
+ {
+ private Configurator Configurator { get; }
+ public AccumulationRegisterTotalEnricher(Configurator configurator)
+ {
+ Configurator = configurator;
+ }
+ public void Enrich(MetadataObject metadataObject)
+ {
+ if (!(metadataObject is AccumulationRegisterTotal register)) throw new ArgumentOutOfRangeException();
+
+ ConfigObject configObject = Configurator.FileReader.ReadConfigObject(register.FileName.ToString());
+
+ register.Name = configObject.GetString(new[] { 1, 13, 1, 2 })
+ + ".Итоги";
+ ConfigObject alias = configObject.GetObject(new[] { 1, 13, 1, 3 });
+ if (alias.Values.Count == 3)
+ {
+ register.Alias = configObject.GetString(new[] { 1, 13, 1, 3, 2 })
+ + " (Итоги)";
+ }
+
+ Configurator.ConfigurePropertyПериод(register);
+
+ // 7 - коллекция измерений
+ ConfigObject properties = configObject.GetObject(new[] { 7 });
+ // 7.0 = b64d9a43-1642-11d6-a3c7-0050bae0a776 - идентификатор коллекции измерений
+ Guid propertiesUuid = configObject.GetUuid(new[] { 7, 0 });
+ if (propertiesUuid == new Guid("b64d9a43-1642-11d6-a3c7-0050bae0a776"))
+ {
+ Configurator.ConfigureProperties(register, properties, PropertyPurpose.Dimension);
+ }
+
+ // 5 - коллекция ресурсов
+ properties = configObject.GetObject(new[] { 5 });
+ // 5.0 = b64d9a41-1642-11d6-a3c7-0050bae0a776 - идентификатор коллекции ресурсов
+ propertiesUuid = configObject.GetUuid(new[] { 5, 0 });
+ if (propertiesUuid == new Guid("b64d9a41-1642-11d6-a3c7-0050bae0a776"))
+ {
+ Configurator.ConfigureProperties(register, properties, PropertyPurpose.Measure);
+ }
+
+ Configurator.ConfigureSharedProperties(register);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/CatalogEnricher.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/CatalogEnricher.cs
new file mode 100644
index 0000000..0c9f1cc
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/CatalogEnricher.cs
@@ -0,0 +1,94 @@
+using System;
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects;
+using YPermitin.SQLCLR.YellowMetadataReader.Services;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Enrichers
+{
+ public sealed class CatalogEnricher : IContentEnricher
+ {
+ private Configurator Configurator { get; }
+ public CatalogEnricher(Configurator configurator)
+ {
+ Configurator = configurator;
+ }
+ public void Enrich(MetadataObject metadataObject)
+ {
+ if (!(metadataObject is Catalog catalog)) throw new ArgumentOutOfRangeException();
+
+ ConfigObject configObject = Configurator.FileReader.ReadConfigObject(catalog.FileName.ToString());
+
+ catalog.Uuid = configObject.GetUuid(new[] { 1, 3 });
+ catalog.Name = configObject.GetString(new[] { 1, 9, 1, 2 });
+ ConfigObject alias = configObject.GetObject(new[] { 1, 9, 1, 3 });
+ if (alias.Values.Count == 3)
+ {
+ catalog.Alias = configObject.GetString(new[] { 1, 9, 1, 3, 2 });
+ }
+ catalog.CodeType = (CodeType)configObject.GetInt32(new[] { 1, 18 });
+ catalog.CodeLength = configObject.GetInt32(new[] { 1, 17 });
+ catalog.DescriptionLength = configObject.GetInt32(new[] { 1, 19 });
+ catalog.HierarchyType = (HierarchyType)configObject.GetInt32(new[] { 1, 36 });
+ catalog.IsHierarchical = configObject.GetInt32(new[] { 1, 37 }) != 0;
+
+ Configurator.ConfigurePropertyСсылка(catalog);
+ Configurator.ConfigurePropertyВерсияДанных(catalog);
+ Configurator.ConfigurePropertyПометкаУдаления(catalog);
+ Configurator.ConfigurePropertyПредопределённый(catalog);
+
+ // 1.12.1 - количество владельцев справочника
+ // 1.12.N - описание владельцев
+ // 1.12.N.2.1 - uuid'ы владельцев (file names)
+ Guid ownerUuid = Guid.Empty;
+ catalog.Owners = configObject.GetInt32(new[] { 1, 12, 1 });
+ if (catalog.Owners == 1)
+ {
+ ownerUuid = configObject.GetUuid(new[] { 1, 12, 2, 2, 1 });
+ }
+ if (catalog.Owners > 0)
+ {
+ Configurator.ConfigurePropertyВладелец(catalog, ownerUuid);
+ }
+
+ if (catalog.CodeLength > 0)
+ {
+ Configurator.ConfigurePropertyКод(catalog);
+ }
+ if (catalog.DescriptionLength > 0)
+ {
+ Configurator.ConfigurePropertyНаименование(catalog);
+ }
+ if (catalog.IsHierarchical)
+ {
+ Configurator.ConfigurePropertyРодитель(catalog);
+ if (catalog.HierarchyType == HierarchyType.Groups)
+ {
+ Configurator.ConfigurePropertyЭтоГруппа(catalog);
+ }
+ }
+
+ // 6 - коллекция реквизитов справочника
+ ConfigObject properties = configObject.GetObject(new[] { 6 });
+ // 6.0 = cf4abea7-37b2-11d4-940f-008048da11f9 - идентификатор коллекции реквизитов справочника
+ Guid propertiesUuid = configObject.GetUuid(new[] { 6, 0 });
+ if (propertiesUuid == new Guid("cf4abea7-37b2-11d4-940f-008048da11f9"))
+ {
+ Configurator.ConfigureProperties(catalog, properties, PropertyPurpose.Property);
+ }
+
+ Configurator.ConfigureSharedProperties(catalog);
+
+ // 5 - коллекция табличных частей справочника
+ ConfigObject tableParts = configObject.GetObject(new[] { 5 });
+ // 5.0 = 932159f9-95b2-4e76-a8dd-8849fe5c5ded - идентификатор коллекции табличных частей справочника
+ Guid collectionUuid = configObject.GetUuid(new[] { 5, 0 });
+ if (collectionUuid == new Guid("932159f9-95b2-4e76-a8dd-8849fe5c5ded"))
+ {
+ Configurator.ConfigureTableParts(catalog, tableParts);
+ }
+
+ Configurator.ConfigurePredefinedValues(catalog);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/CharacteristicEnricher.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/CharacteristicEnricher.cs
new file mode 100644
index 0000000..cff65c7
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/CharacteristicEnricher.cs
@@ -0,0 +1,87 @@
+using System;
+using YPermitin.SQLCLR.YellowMetadataReader.Enrichers.Converters;
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects;
+using YPermitin.SQLCLR.YellowMetadataReader.Services;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Enrichers
+{
+ public sealed class CharacteristicEnricher : IContentEnricher
+ {
+ private Configurator Configurator { get; }
+ private IConfigObjectConverter TypeInfoConverter { get; }
+ public CharacteristicEnricher(Configurator configurator)
+ {
+ Configurator = configurator;
+ TypeInfoConverter = Configurator.GetConverter();
+ }
+ public void Enrich(MetadataObject metadataObject)
+ {
+ if (!(metadataObject is Characteristic model)) throw new ArgumentOutOfRangeException();
+
+ ConfigObject configObject = Configurator.FileReader.ReadConfigObject(model.FileName.ToString());
+
+ model.Uuid = configObject.GetUuid(new[] { 1, 3 });
+ model.TypeUuid = configObject.GetUuid(new[] { 1, 9 });
+ model.Name = configObject.GetString(new[] { 1, 13, 1, 2 });
+ ConfigObject alias = configObject.GetObject(new[] { 1, 13, 1, 3 });
+ if (alias.Values.Count == 3)
+ {
+ model.Alias = configObject.GetString(new[] { 1, 13, 1, 3, 2 });
+ }
+ model.CodeLength = configObject.GetInt32(new[] { 1, 21 });
+ model.DescriptionLength = configObject.GetInt32(new[] { 1, 23 });
+ model.IsHierarchical = configObject.GetInt32(new[] { 1, 19 }) != 0;
+
+ Configurator.ConfigurePropertyСсылка(model);
+ Configurator.ConfigurePropertyВерсияДанных(model);
+ Configurator.ConfigurePropertyПометкаУдаления(model);
+ Configurator.ConfigurePropertyПредопределённый(model);
+ Configurator.ConfigurePropertyТипЗначения(model);
+
+ if (model.CodeLength > 0)
+ {
+ Configurator.ConfigurePropertyКод(model);
+ }
+ if (model.DescriptionLength > 0)
+ {
+ Configurator.ConfigurePropertyНаименование(model);
+ }
+ if (model.IsHierarchical)
+ {
+ Configurator.ConfigurePropertyРодитель(model);
+ if (model.HierarchyType == HierarchyType.Groups)
+ {
+ Configurator.ConfigurePropertyЭтоГруппа(model);
+ }
+ }
+
+ // 1.18 - описание типов значений характеристики
+ ConfigObject propertyTypes = configObject.GetObject(new[] { 1, 18 });
+ model.TypeInfo = (DataTypeInfo)TypeInfoConverter.Convert(propertyTypes);
+
+ // 3 - коллекция реквизитов
+ ConfigObject properties = configObject.GetObject(new[] { 3 });
+ // 3.0 = 31182525-9346-4595-81f8-6f91a72ebe06 - идентификатор коллекции реквизитов
+ Guid propertiesUuid = configObject.GetUuid(new[] { 3, 0 });
+ if (propertiesUuid == new Guid("31182525-9346-4595-81f8-6f91a72ebe06"))
+ {
+ Configurator.ConfigureProperties(model, properties, PropertyPurpose.Property);
+ }
+
+ Configurator.ConfigureSharedProperties(model);
+
+ // 5 - коллекция табличных частей
+ ConfigObject tableParts = configObject.GetObject(new[] { 5 });
+ // 5.0 = 54e36536-7863-42fd-bea3-c5edd3122fdc - идентификатор коллекции табличных частей
+ Guid collectionUuid = configObject.GetUuid(new[] { 5, 0 });
+ if (collectionUuid == new Guid("54e36536-7863-42fd-bea3-c5edd3122fdc"))
+ {
+ Configurator.ConfigureTableParts(model, tableParts);
+ }
+
+ Configurator.ConfigurePredefinedValues(model);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/ConstantEnricher.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/ConstantEnricher.cs
new file mode 100644
index 0000000..4be11fe
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/ConstantEnricher.cs
@@ -0,0 +1,28 @@
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+using YPermitin.SQLCLR.YellowMetadataReader.Services;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Enrichers
+{
+ public sealed class ConstantEnricher : IContentEnricher
+ {
+ private Configurator Configurator { get; }
+ public ConstantEnricher(Configurator configurator)
+ {
+ Configurator = configurator;
+ }
+ public void Enrich(MetadataObject metadataObject)
+ {
+ ConfigObject configObject = Configurator.FileReader.ReadConfigObject(metadataObject.FileName.ToString());
+
+ if (configObject == null) return; // TODO: log error
+
+ metadataObject.Uuid = configObject.GetUuid(new[] { 1, 2 });
+ metadataObject.Name = configObject.GetString(new[] { 1, 1, 1, 1, 2 });
+ ConfigObject alias = configObject.GetObject(new[] { 1, 1, 1, 1, 3 });
+ if (alias.Values.Count == 3)
+ {
+ metadataObject.Alias = configObject.GetString(new[] { 2 });
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/Converters/DataTypeInfoConverter.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/Converters/DataTypeInfoConverter.cs
new file mode 100644
index 0000000..c5c1a37
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/Converters/DataTypeInfoConverter.cs
@@ -0,0 +1,149 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects;
+using YPermitin.SQLCLR.YellowMetadataReader.Services;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Enrichers.Converters
+{
+ public sealed class DataTypeInfoConverter : IConfigObjectConverter
+ {
+ private readonly Dictionary> _referenceBaseTypes = new Dictionary>();
+ private Configurator Configurator { get; }
+ public DataTypeInfoConverter(Configurator configurator)
+ {
+ Configurator = configurator;
+ _referenceBaseTypes.Add(new Guid("280f5f0e-9c8a-49cc-bf6d-4d296cc17a63"), null); // ЛюбаяСсылка
+ _referenceBaseTypes.Add(new Guid("e61ef7b8-f3e1-4f4b-8ac7-676e90524997"), Configurator.InfoBase.Catalogs); // СправочникСсылка
+ _referenceBaseTypes.Add(new Guid("38bfd075-3e63-4aaa-a93e-94521380d579"), Configurator.InfoBase.Documents); // ДокументСсылка
+ _referenceBaseTypes.Add(new Guid("474c3bf6-08b5-4ddc-a2ad-989cedf11583"), Configurator.InfoBase.Enumerations); // ПеречислениеСсылка
+ _referenceBaseTypes.Add(new Guid("0a52f9de-73ea-4507-81e8-66217bead73a"), Configurator.InfoBase.Publications); // ПланОбменаСсылка
+ _referenceBaseTypes.Add(new Guid("99892482-ed55-4fb5-a7f7-20888820a758"), Configurator.InfoBase.Characteristics); // ПланВидовХарактеристикСсылка
+ _referenceBaseTypes.Add(new Guid("ac606d60-0209-4159-8e4c-794bc091ce38"), Configurator.InfoBase.Accounts); // ПланСчетовСсылка
+ }
+ public object Convert(ConfigObject configObject)
+ {
+ DataTypeInfo typeInfo = new DataTypeInfo();
+
+ // 0 = "Pattern"
+ int typeOffset = 1;
+ List typeUuids = new List();
+ int count = configObject.Values.Count - 1;
+
+ for (int t = 0; t < count; t++)
+ {
+ // T - type descriptor
+ ConfigObject descriptor = configObject.GetObject(new[] { t + typeOffset });
+
+ // T.Q - property type qualifiers
+ string[] qualifiers = new string[descriptor.Values.Count];
+ for (int q = 0; q < descriptor.Values.Count; q++)
+ {
+ qualifiers[q] = configObject.GetString(new[] { t + typeOffset, q });
+ }
+ if (qualifiers[0] == MetadataTokens.B) typeInfo.CanBeBoolean = true; // {"B"}
+ else if (qualifiers[0] == MetadataTokens.S)
+ {
+ typeInfo.CanBeString = true; // {"S"} | {"S",10,0} | {"S",10,1}
+ if (qualifiers.Length == 1)
+ {
+ typeInfo.StringLength = -1;
+ typeInfo.StringKind = StringKind.Unlimited;
+ }
+ else
+ {
+ typeInfo.StringLength = int.Parse(qualifiers[1]);
+ typeInfo.StringKind = (StringKind)int.Parse(qualifiers[2]);
+ }
+ }
+ else if (qualifiers[0] == MetadataTokens.N)
+ {
+ typeInfo.CanBeNumeric = true; // {"N",10,2,0} | {"N",10,2,1}
+ typeInfo.NumericPrecision = int.Parse(qualifiers[1]);
+ typeInfo.NumericScale = int.Parse(qualifiers[2]);
+ typeInfo.NumericKind = (NumericKind)int.Parse(qualifiers[3]);
+ }
+ else if (qualifiers[0] == MetadataTokens.D)
+ {
+ typeInfo.CanBeDateTime = true; // {"D"} | {"D","D"} | {"D","T"}
+ if (qualifiers.Length == 1)
+ {
+ typeInfo.DateTimePart = DateTimePart.DateTime;
+ }
+ else if (qualifiers[1] == MetadataTokens.D)
+ {
+ typeInfo.DateTimePart = DateTimePart.Date;
+ }
+ else
+ {
+ typeInfo.DateTimePart = DateTimePart.Time;
+ }
+ }
+ else if (qualifiers[0] == MetadataTokens.R) // {"#",70497451-981e-43b8-af46-fae8d65d16f2}
+ {
+ Guid typeUuid = new Guid(qualifiers[1]);
+ if (typeUuid == new Guid("e199ca70-93cf-46ce-a54b-6edc88c3a296")) // ХранилищеЗначения - varbinary(max)
+ {
+ typeInfo.IsValueStorage = true;
+ }
+ else if (typeUuid == new Guid("fc01b5df-97fe-449b-83d4-218a090e681e")) // УникальныйИдентификатор - binary(16)
+ {
+ typeInfo.IsUuid = true;
+ }
+ else if (_referenceBaseTypes.TryGetValue(typeUuid, out Dictionary collection))
+ {
+ if (collection == null) // Любая ссылка
+ {
+ typeInfo.CanBeReference = true;
+ typeUuids.Add(Guid.Empty);
+ }
+ else if (collection.Count == 1) // Единственный объект метаданных в коллекции
+ {
+ typeInfo.CanBeReference = true;
+ typeUuids.Add(collection.Values.First().Uuid);
+ }
+ else // Множественный ссылочный тип данных
+ {
+ typeInfo.CanBeReference = true;
+ typeUuids.Add(Guid.Empty);
+ }
+ }
+ else if (Configurator.InfoBase.CompoundTypes.TryGetValue(typeUuid, out CompoundType compound))
+ {
+ // since 8.3.3
+ Configurator.InfoBase.ApplyCompoundType(typeInfo, compound);
+ typeUuids.Add(compound.TypeInfo.ReferenceTypeUuid);
+ }
+ else if (Configurator.InfoBase.CharacteristicTypes.TryGetValue(typeUuid, out Characteristic characteristic))
+ {
+ Configurator.InfoBase.ApplyCharacteristic(typeInfo, characteristic);
+ typeUuids.Add(characteristic.TypeInfo.ReferenceTypeUuid);
+ }
+ //else if (Configurator.InfoBase.ReferenceTypeUuids.TryGetValue(typeUuid, out ApplicationObject metaObject))
+ //{
+ // typeInfo.CanBeReference = true;
+ // typeUuids.Add(typeUuid);
+ //}
+ else
+ {
+ // идентификатор ссылочного типа данных (см. закомментированную ветку выше)
+ // или
+ // идентификатор типа данных (определяемый или характеристика) ещё не загружен
+ // или
+ // неизвестный тип данных (работа с этим типом данных не реализована)
+ typeInfo.CanBeReference = true;
+ typeUuids.Add(typeUuid);
+ }
+ }
+ }
+ if (typeUuids.Count == 1) // single type value
+ {
+ typeInfo.ReferenceTypeUuid = typeUuids[0];
+ }
+
+ return typeInfo;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/Converters/IConfigObjectConverter.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/Converters/IConfigObjectConverter.cs
new file mode 100644
index 0000000..8e9c6f3
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/Converters/IConfigObjectConverter.cs
@@ -0,0 +1,9 @@
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Enrichers.Converters
+{
+ public interface IConfigObjectConverter
+ {
+ object Convert(ConfigObject configObject);
+ }
+}
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/DbNamesEnricher.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/DbNamesEnricher.cs
new file mode 100644
index 0000000..22ce560
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/DbNamesEnricher.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Collections.Generic;
+using YPermitin.SQLCLR.YellowMetadataReader.Helpers;
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+using YPermitin.SQLCLR.YellowMetadataReader.Services;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Enrichers
+{
+ public sealed class DbNamesEnricher : IContentEnricher
+ {
+ // ReSharper disable once InconsistentNaming
+ private const string DBNAMES_FILE_NAME = "DBNames"; // Params
+ private Configurator Configurator { get; }
+ public DbNamesEnricher(Configurator configurator)
+ {
+ Configurator = configurator;
+ }
+ public void Enrich(MetadataObject metadataObject)
+ {
+ if (!(metadataObject is InfoBase infoBase)) throw new ArgumentOutOfRangeException();
+
+ ConfigObject configObject = Configurator.FileReader.ReadConfigObject(DBNAMES_FILE_NAME);
+
+ int entryCount = configObject.GetInt32(new[] { 1, 0 });
+
+ for (int i = 1; i <= entryCount; i++)
+ {
+ Guid uuid = configObject.GetUuid(new[] { 1, i, 0 });
+
+ string token = configObject.GetString(new[] { 1, i, 1 });
+
+ int code;
+ if (uuid == Guid.Empty)
+ {
+ code = 0;
+ }
+ else
+ {
+ code = configObject.GetInt32(new[] { 1, i, 2 });
+ }
+
+ ProcessEntry(infoBase, uuid, token, code);
+ }
+ }
+ private void ProcessEntry(InfoBase infoBase, Guid uuid, string token, int code)
+ {
+ string fullKey = GeneralHelper.GenerateConfigFullObjectKey(uuid, token, code);
+
+ if (token.StartsWith("ByField")
+ || token.StartsWith("ByOwnerField")
+ || token.StartsWith("ByParentField")
+ || token.StartsWith("ByDims")
+ || token.StartsWith("ByProperty")
+ || token.StartsWith("ByResource")
+ || token.StartsWith("EDBT")
+ || token.StartsWith("Consts")
+ || token.StartsWith("UsersDmm"))
+ {
+ // Индексы пропускаем
+ return;
+ }
+
+ if (token == MetadataTokens.Fld || token == MetadataTokens.LineNo)
+ {
+ if (!infoBase.Properties.ContainsKey(fullKey))
+ {
+ var propertyObject = Configurator.CreateProperty(uuid, token, code);
+ infoBase.Properties.Add(fullKey, propertyObject);
+ if (propertyObject.FileName != Guid.Empty)
+ {
+ if (!infoBase.PropertiesById.ContainsKey(propertyObject.FileName))
+ infoBase.PropertiesById.Add(propertyObject.FileName, propertyObject);
+ }
+ }
+
+ return;
+ }
+
+ Type type = Configurator.GetTypeByToken(token);
+ ApplicationObject metaObject = Configurator.CreateObject(uuid, token, code);
+ if (metaObject == null) return; // unsupported type of metadata object
+
+ if (token == MetadataTokens.VT)
+ {
+ if (!infoBase.TableParts.ContainsKey(fullKey))
+ {
+ infoBase.TableParts.Add(fullKey, metaObject);
+ if (metaObject.FileName != Guid.Empty)
+ {
+ if(!infoBase.TablePartsById.ContainsKey(metaObject.FileName))
+ infoBase.TablePartsById.Add(metaObject.FileName, metaObject);
+ }
+ }
+ return;
+ }
+
+ if (token == MetadataTokens.AccumRgT)
+ {
+ if(infoBase.AllObjectsById.TryGetValue(metaObject.FileName, out ApplicationObject foundObject))
+ {
+ foundObject.NestedObjects.Add(metaObject);
+ }
+
+ return;
+ }
+
+ if (!infoBase.AllTypes.TryGetValue(type, out Dictionary collection))
+ {
+ return; // unsupported collection of metadata objects
+ }
+
+ if (!collection.ContainsKey(fullKey))
+ {
+ collection.Add(fullKey, metaObject);
+ if (metaObject.FileName != Guid.Empty)
+ {
+ if (!infoBase.AllObjectsById.ContainsKey(metaObject.FileName))
+ infoBase.AllObjectsById.Add(metaObject.FileName, metaObject);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/DocumentEnricher.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/DocumentEnricher.cs
new file mode 100644
index 0000000..64ffc26
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/DocumentEnricher.cs
@@ -0,0 +1,74 @@
+using System;
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects;
+using YPermitin.SQLCLR.YellowMetadataReader.Services;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Enrichers
+{
+ public sealed class DocumentEnricher : IContentEnricher
+ {
+ private Configurator Configurator { get; }
+ public DocumentEnricher(Configurator configurator)
+ {
+ Configurator = configurator;
+ }
+ public void Enrich(MetadataObject metadataObject)
+ {
+ if (!(metadataObject is Document document)) throw new ArgumentOutOfRangeException();
+
+ ConfigObject configObject = Configurator.FileReader.ReadConfigObject(document.FileName.ToString());
+
+ if (configObject == null) return; // TODO: log error
+
+ document.Uuid = configObject.GetUuid(new[] { 1, 3 });
+ document.Name = configObject.GetString(new[] { 1, 9, 1, 2 });
+ ConfigObject alias = configObject.GetObject(new[] { 1, 9, 1, 3 });
+ if (alias.Values.Count == 3)
+ {
+ document.Alias = configObject.GetString(new[] { 1, 9, 1, 3, 2 });
+ }
+
+ document.NumberType = (NumberType)configObject.GetInt32(new[] { 1, 11 });
+ document.NumberLength = configObject.GetInt32(new[] { 1, 12 });
+ document.Periodicity = (Periodicity)configObject.GetInt32(new[] { 1, 13 });
+
+ Configurator.ConfigurePropertyСсылка(document);
+ Configurator.ConfigurePropertyВерсияДанных(document);
+ Configurator.ConfigurePropertyПометкаУдаления(document);
+ Configurator.ConfigurePropertyДата(document);
+ if (document.NumberLength > 0)
+ {
+ if (document.Periodicity != Periodicity.None)
+ {
+ Configurator.ConfigurePropertyПериодичность(document);
+ }
+ Configurator.ConfigurePropertyНомер(document);
+ }
+ Configurator.ConfigurePropertyПроведён(document);
+
+ ConfigObject registers = configObject.GetObject(new[] { 1, 24 });
+ Configurator.ConfigureRegistersToPost(document, registers);
+
+ // 5 - коллекция реквизитов
+ ConfigObject properties = configObject.GetObject(new[] { 5 });
+ // 5.0 = 45e46cbc-3e24-4165-8b7b-cc98a6f80211 - идентификатор коллекции реквизитов
+ Guid propertiesUuid = configObject.GetUuid(new[] { 5, 0 });
+ if (propertiesUuid == new Guid("45e46cbc-3e24-4165-8b7b-cc98a6f80211"))
+ {
+ Configurator.ConfigureProperties(document, properties, PropertyPurpose.Property);
+ }
+
+ Configurator.ConfigureSharedProperties(document);
+
+ // 3 - коллекция табличных частей справочника
+ ConfigObject tableParts = configObject.GetObject(new[] { 3 });
+ // 3.0 = 21c53e09-8950-4b5e-a6a0-1054f1bbc274 - идентификатор коллекции табличных частей
+ Guid collectionUuid = configObject.GetUuid(new[] { 3, 0 });
+ if (collectionUuid == new Guid("21c53e09-8950-4b5e-a6a0-1054f1bbc274"))
+ {
+ Configurator.ConfigureTableParts(document, tableParts);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/EnumerationEnricher.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/EnumerationEnricher.cs
new file mode 100644
index 0000000..59c352d
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/EnumerationEnricher.cs
@@ -0,0 +1,75 @@
+using System;
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects;
+using YPermitin.SQLCLR.YellowMetadataReader.Services;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Enrichers
+{
+ public sealed class EnumerationEnricher : IContentEnricher
+ {
+ private Configurator Configurator { get; }
+ public EnumerationEnricher(Configurator configurator)
+ {
+ Configurator = configurator;
+ }
+ public void Enrich(MetadataObject metadataObject)
+ {
+ if (!(metadataObject is Enumeration enumeration)) throw new ArgumentOutOfRangeException();
+
+ ConfigObject configObject = Configurator.FileReader.ReadConfigObject(enumeration.FileName.ToString());
+
+ if (configObject == null) return; // TODO: log error
+
+ enumeration.Uuid = configObject.GetUuid(new[] { 1, 1 });
+ enumeration.Name = configObject.GetString(new[] { 1, 5, 1, 2 });
+ ConfigObject alias = configObject.GetObject(new[] { 1, 5, 1, 3 });
+ if (alias.Values.Count == 3)
+ {
+ enumeration.Alias = configObject.GetString(new[] { 1, 5, 1, 3, 2 });
+ }
+ Configurator.ConfigurePropertyСсылка(enumeration);
+ Configurator.ConfigurePropertyПорядок(enumeration);
+
+ // 6 - коллекция значений
+ ConfigObject values = configObject.GetObject(new[] { 6 });
+ // 6.0 = bee0a08c-07eb-40c0-8544-5c364c171465 - идентификатор коллекции значений
+ Guid valuesUuid = configObject.GetUuid(new[] { 6, 0 });
+ if (valuesUuid == new Guid("bee0a08c-07eb-40c0-8544-5c364c171465"))
+ {
+ ConfigureValues(enumeration, values);
+ }
+ }
+ private void ConfigureValues(Enumeration enumeration, ConfigObject values)
+ {
+ int valuesCount = values.GetInt32(new[] { 1 }); // количество значений
+ if (valuesCount == 0) return;
+
+ int offset = 2;
+ int orderNumber = 0;
+ for (int v = 0; v < valuesCount; v++)
+ {
+ // V.0.1.1.2 - value uuid
+ Guid uuid = values.GetUuid(new[] { v + offset, 0, 1, 1, 2 });
+ // V.0.1.2 - value name
+ string name = values.GetString(new[] { v + offset, 0, 1, 2 });
+ // P.0.1.3 - value alias descriptor
+ string alias = string.Empty;
+ ConfigObject aliasDescriptor = values.GetObject(new[] { v + offset, 0, 1, 3 });
+ if (aliasDescriptor.Values.Count == 3)
+ {
+ // P.0.1.3.2 - value alias
+ alias = values.GetString(new[] { v + offset, 0, 1, 3, 2 });
+ }
+ enumeration.Values.Add(new EnumValue()
+ {
+ Uuid = uuid,
+ Name = name,
+ Alias = alias,
+ OrderNumber = orderNumber
+ });
+
+ orderNumber += 1;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/IContentEnricher.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/IContentEnricher.cs
new file mode 100644
index 0000000..5507234
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/IContentEnricher.cs
@@ -0,0 +1,9 @@
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Enrichers
+{
+ public interface IContentEnricher
+ {
+ void Enrich(MetadataObject metadataObject);
+ }
+}
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/InfoBaseEnricher.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/InfoBaseEnricher.cs
new file mode 100644
index 0000000..bf1b87f
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/InfoBaseEnricher.cs
@@ -0,0 +1,203 @@
+using System;
+using YPermitin.SQLCLR.YellowMetadataReader.Enrichers.Converters;
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects;
+using YPermitin.SQLCLR.YellowMetadataReader.Services;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Enrichers
+{
+ public sealed class InfoBaseEnricher : IContentEnricher
+ {
+ private const string RootFileName = "root"; // Config
+ private Configurator Configurator { get; }
+ private IConfigFileReader FileReader { get; }
+ private IConfigObjectConverter TypeInfoConverter { get; }
+ public InfoBaseEnricher(Configurator configurator)
+ {
+ Configurator = configurator;
+ FileReader = Configurator.FileReader;
+ TypeInfoConverter = Configurator.GetConverter();
+ }
+ public void Enrich(MetadataObject metadataObject)
+ {
+ if (!(metadataObject is InfoBase infoBase)) throw new ArgumentOutOfRangeException();
+
+ infoBase.YearOffset = FileReader.GetYearOffset();
+ infoBase.PlatformRequiredVersion = FileReader.GetPlatformRequiredVersion();
+
+ ConfigObject root = FileReader.ReadConfigObject(RootFileName);
+ string fileName = root.GetString(new[] { 1 });
+ ConfigObject config = FileReader.ReadConfigObject(fileName);
+
+ infoBase.Uuid = new Guid(fileName);
+ infoBase.FileName = infoBase.Uuid;
+
+ ConfigureConfigInfo(infoBase, config);
+ ConfigureCommonObjects(infoBase, config);
+
+ if (string.IsNullOrWhiteSpace(infoBase.Name) && infoBase.ConfigInfo != null)
+ {
+ infoBase.Name = infoBase.ConfigInfo.Name;
+ }
+ }
+
+ private void ConfigureConfigInfo(InfoBase infoBase, ConfigObject config)
+ {
+ ConfigInfo info = new ConfigInfo();
+
+ info.Name = config.GetString(new[] { 3, 1, 1, 1, 1, 2 }); // Имя
+ ConfigObject alias = config.GetObject(new[] { 3, 1, 1, 1, 1, 3 });
+ if (alias.Values.Count == 3)
+ {
+ info.Alias = alias.GetString(new[] { 2 }); // Синоним
+ }
+ info.Comment = config.GetString(new[] { 3, 1, 1, 1, 1, 4 }); // Комментарий
+
+ int version = config.GetInt32(new[] { 3, 1, 1, 26 }); // Режим совместимости
+ if (version == 0) info.Version = 80216;
+ else if (version == 1) info.Version = 80100;
+ else if (version == 2) info.Version = 80213;
+ else info.Version = version;
+ // Версия конфигурации
+ info.ConfigVersion = config.GetString(new[] { 3, 1, 1, 15 });
+ if (info.Version == 80216)
+ {
+ // Режим управления блокировкой данных в транзакции по умолчанию
+ info.DataLockingMode = (DataLockingMode)config.GetInt32(new[] { 3, 1, 1, 17 });
+ // Режим автонумерации объектов
+ info.AutoNumberingMode = (AutoNumberingMode)config.GetInt32(new[] { 3, 1, 1, 19 });
+ }
+ else
+ {
+ // Режим использования синхронных вызовов расширений платформы и внешних компонент
+ info.SyncCallsMode = (SyncCallsMode)config.GetInt32(new[] { 3, 1, 1, 41 });
+ // Режим использования модальности
+ info.ModalWindowMode = (ModalWindowMode)config.GetInt32(new[] { 3, 1, 1, 36 });
+ // Режим управления блокировкой данных в транзакции по умолчанию
+ info.DataLockingMode = (DataLockingMode)config.GetInt32(new[] { 3, 1, 1, 17 });
+ // Режим автонумерации объектов
+ info.AutoNumberingMode = (AutoNumberingMode)config.GetInt32(new[] { 3, 1, 1, 19 });
+ // Режим совместимости интерфейса
+ info.UiCompatibilityMode = (UiCompatibilityMode)config.GetInt32(new[] { 3, 1, 1, 38 });
+ }
+
+ infoBase.ConfigInfo = info;
+ }
+
+ private void ConfigureCommonObjects(InfoBase infoBase, ConfigObject config)
+ {
+ // 3.1.2 - количество объектов метаданных ветки "Общие" для данной версии платформы
+ int count = config.GetInt32(new[] { 3, 1, 2 });
+ int offset = 3;
+ for (int i = 0; i < count; i++)
+ {
+ ConfigObject collection = config.GetObject(new[] { 3, 1, i + offset }); // узел объекта метаданных
+
+ Guid uuid = collection.GetUuid(new[] { 0 }); // идентификатор типа объекта метаданных
+
+ if (uuid == new Guid("15794563-ccec-41f6-a83c-ec5f7b9a5bc1")) // идентификатор коллекции общих реквизитов
+ {
+ ConfigureSharedProperties(infoBase, collection); // Общие реквизиты
+ }
+ else if (uuid == new Guid("c045099e-13b9-4fb6-9d50-fca00202971e")) // идентификатор коллекции определяемых типов
+ {
+ ConfigureCompoundTypes(infoBase, collection); // Определяемые типы
+ }
+ }
+ }
+
+ private void ConfigureSharedProperties(InfoBase infoBase, ConfigObject collection)
+ {
+ // количество объектов в коллекции
+ int count = collection.GetInt32(new[] { 1 });
+ if (count == 0) return;
+
+ int offset = 2;
+ SharedProperty property;
+ for (int i = 0; i < count; i++)
+ {
+ property = new SharedProperty()
+ {
+ FileName = collection.GetUuid(new[] { i + offset })
+ };
+ ConfigureSharedProperty(property, infoBase);
+ infoBase.SharedProperties.Add(property.FileName, property);
+ }
+ }
+ private void ConfigureSharedProperty(SharedProperty property, InfoBase infoBase)
+ {
+ ConfigObject cfo = FileReader.ReadConfigObject(property.FileName.ToString());
+
+ if (infoBase.PropertiesById.TryGetValue(property.FileName, out MetadataProperty propertyInfo))
+ {
+ property.DbName = propertyInfo.DbName;
+ }
+ property.Name = cfo.GetString(new[] { 1, 1, 1, 1, 2 });
+ ConfigObject aliasDescriptor = cfo.GetObject(new[] { 1, 1, 1, 1, 3 });
+ if (aliasDescriptor.Values.Count == 3)
+ {
+ property.Alias = cfo.GetString(new[] { 1, 1, 1, 1, 3, 2 });
+ }
+ property.AutomaticUsage = (AutomaticUsage)cfo.GetInt32(new[] { 1, 6 });
+
+ // 1.1.1.2 - описание типов значений общего реквизита
+ ConfigObject propertyTypes = cfo.GetObject(new[] { 1, 1, 1, 2 });
+ property.PropertyType = (DataTypeInfo)TypeInfoConverter.Convert(propertyTypes);
+
+ Configurator.ConfigureDatabaseFields(property);
+
+ // 1.2.1 - количество объектов метаданных, у которых значение использования общего реквизита не равно "Автоматически"
+ int count = cfo.GetInt32(new[] { 1, 2, 1 });
+ if (count == 0) return;
+ int step = 2;
+ count *= step;
+ int uuidIndex = 2;
+ int usageOffset = 1;
+ while (uuidIndex <= count)
+ {
+ Guid uuid = cfo.GetUuid(new[] { 1, 2, uuidIndex });
+ SharedPropertyUsage usage = (SharedPropertyUsage)cfo.GetInt32(new[] { 1, 2, uuidIndex + usageOffset, 1 });
+ property.UsageSettings.Add(uuid, usage);
+ uuidIndex += step;
+ }
+ }
+
+ private void ConfigureCompoundTypes(InfoBase infoBase, ConfigObject collection)
+ {
+ // количество объектов в коллекции
+ int count = collection.GetInt32(new[] { 1 });
+ if (count == 0) return;
+
+ // 3.1.23.N - идентификаторы файлов определяемых типов
+ int offset = 2;
+ CompoundType compound;
+ for (int i = 0; i < count; i++)
+ {
+ compound = new CompoundType()
+ {
+ FileName = collection.GetUuid(new[] { i + offset })
+ };
+ ConfigureCompoundType(compound);
+ infoBase.CompoundTypes.Add(compound.Uuid, compound);
+ }
+ }
+ private void ConfigureCompoundType(CompoundType compound)
+ {
+ ConfigObject cfo = FileReader.ReadConfigObject(compound.FileName.ToString());
+
+ compound.Uuid = cfo.GetUuid(new[] { 1, 1 });
+ compound.Name = cfo.GetString(new[] { 1, 3, 2 });
+ ConfigObject alias = cfo.GetObject(new[] { 1, 3, 3 });
+ if (alias.Values.Count == 3)
+ {
+ compound.Alias = cfo.GetString(new[] { 1, 3, 3, 2 });
+ }
+ // 1.3.4 - комментарий
+
+ // 1.4 - описание типов значений определяемого типа
+ ConfigObject propertyTypes = cfo.GetObject(new[] { 1, 4 });
+ compound.TypeInfo = (DataTypeInfo)TypeInfoConverter.Convert(propertyTypes);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/InformationRegisterEnricher.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/InformationRegisterEnricher.cs
new file mode 100644
index 0000000..e442836
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/InformationRegisterEnricher.cs
@@ -0,0 +1,73 @@
+using System;
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects;
+using YPermitin.SQLCLR.YellowMetadataReader.Services;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Enrichers
+{
+ public sealed class InformationRegisterEnricher : IContentEnricher
+ {
+ private Configurator Configurator { get; }
+ public InformationRegisterEnricher(Configurator configurator)
+ {
+ Configurator = configurator;
+ }
+ public void Enrich(MetadataObject metadataObject)
+ {
+ if (!(metadataObject is InformationRegister register)) throw new ArgumentOutOfRangeException();
+
+ ConfigObject configObject = Configurator.FileReader.ReadConfigObject(register.FileName.ToString());
+
+ if (configObject == null) return; // TODO: log error
+
+ register.Name = configObject.GetString(new[] { 1, 15, 1, 2 });
+ ConfigObject alias = configObject.GetObject(new[] { 1, 15, 1, 3 });
+ if (alias.Values.Count == 3)
+ {
+ register.Alias = configObject.GetString(new[] { 1, 15, 1, 3, 2 });
+ }
+ register.UseRecorder = configObject.GetInt32(new[] { 1, 19 }) != 0;
+ register.Periodicity = (RegisterPeriodicity)configObject.GetInt32(new[] { 1, 18 });
+
+ if (register.Periodicity != RegisterPeriodicity.None)
+ {
+ Configurator.ConfigurePropertyПериод(register);
+ }
+ if (register.UseRecorder)
+ {
+ Configurator.ConfigurePropertyНомерЗаписи(register);
+ Configurator.ConfigurePropertyАктивность(register);
+ }
+
+ // 4 - коллекция измерений
+ ConfigObject properties = configObject.GetObject(new[] { 4 });
+ // 4.0 = 13134203-f60b-11d5-a3c7-0050bae0a776 - идентификатор коллекции измерений
+ Guid propertiesUuid = configObject.GetUuid(new[] { 4, 0 });
+ if (propertiesUuid == new Guid("13134203-f60b-11d5-a3c7-0050bae0a776"))
+ {
+ Configurator.ConfigureProperties(register, properties, PropertyPurpose.Dimension);
+ }
+
+ // 3 - коллекция ресурсов
+ properties = configObject.GetObject(new[] { 3 });
+ // 3.0 = 13134202-f60b-11d5-a3c7-0050bae0a776 - идентификатор коллекции ресурсов
+ propertiesUuid = configObject.GetUuid(new[] { 3, 0 });
+ if (propertiesUuid == new Guid("13134202-f60b-11d5-a3c7-0050bae0a776"))
+ {
+ Configurator.ConfigureProperties(register, properties, PropertyPurpose.Measure);
+ }
+
+ // 7 - коллекция реквизитов
+ properties = configObject.GetObject(new[] { 7 });
+ // 7.0 = a2207540-1400-11d6-a3c7-0050bae0a776 - идентификатор коллекции реквизитов
+ propertiesUuid = configObject.GetUuid(new[] { 7, 0 });
+ if (propertiesUuid == new Guid("a2207540-1400-11d6-a3c7-0050bae0a776"))
+ {
+ Configurator.ConfigureProperties(register, properties, PropertyPurpose.Property);
+ }
+
+ Configurator.ConfigureSharedProperties(register);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/PublicationEnricher.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/PublicationEnricher.cs
new file mode 100644
index 0000000..88a70e0
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/PublicationEnricher.cs
@@ -0,0 +1,85 @@
+using System;
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects;
+using YPermitin.SQLCLR.YellowMetadataReader.Services;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Enrichers
+{
+ public sealed class PublicationEnricher : IContentEnricher
+ {
+ private Configurator Configurator { get; }
+ public PublicationEnricher(Configurator configurator)
+ {
+ Configurator = configurator;
+ }
+ public void Enrich(MetadataObject metadataObject)
+ {
+ if (!(metadataObject is Publication publication)) throw new ArgumentOutOfRangeException();
+
+ ConfigObject configObject = Configurator.FileReader.ReadConfigObject(publication.FileName.ToString());
+
+ if (configObject == null) return; // TODO: log error
+
+ publication.Uuid = configObject.GetUuid(new[] { 1, 3 });
+ publication.Name = configObject.GetString(new[] { 1, 12, 2 });
+ ConfigObject alias = configObject.GetObject(new[] { 1, 12, 3 });
+ if (alias.Values.Count == 3)
+ {
+ publication.Alias = configObject.GetString(new[] { 1, 12, 3, 2 });
+ }
+ publication.CodeLength = configObject.GetInt32(new[] { 1, 15 });
+ publication.DescriptionLength = configObject.GetInt32(new[] { 1, 17 });
+ publication.IsDistributed = configObject.GetInt32(new[] { 1, 26 }) != 0;
+
+ Configurator.ConfigurePropertyСсылка(publication);
+ Configurator.ConfigurePropertyВерсияДанных(publication);
+ Configurator.ConfigurePropertyПометкаУдаления(publication);
+ Configurator.ConfigurePropertyКод(publication);
+ Configurator.ConfigurePropertyНаименование(publication);
+ Configurator.ConfigurePropertyНомерОтправленного(publication);
+ Configurator.ConfigurePropertyНомерПринятого(publication);
+ Configurator.ConfigurePropertyПредопределённый(publication);
+
+ // 3 - коллекция реквизитов
+ ConfigObject properties = configObject.GetObject(new[] { 3 });
+ // 3.0 = 1a1b4fea-e093-470d-94ff-1d2f16cda2ab - идентификатор коллекции реквизитов
+ Guid propertiesUuid = configObject.GetUuid(new[] { 3, 0 });
+ if (propertiesUuid == new Guid("1a1b4fea-e093-470d-94ff-1d2f16cda2ab"))
+ {
+ Configurator.ConfigureProperties(publication, properties, PropertyPurpose.Property);
+ }
+
+ Configurator.ConfigureSharedProperties(publication);
+
+ // 5 - коллекция табличных частей
+ ConfigObject tableParts = configObject.GetObject(new[] { 5 });
+ // 5.0 = 52293f4b-f98c-43ea-a80f-41047ae7ab58 - идентификатор коллекции табличных частей
+ Guid collectionUuid = configObject.GetUuid(new[] { 5, 0 });
+ if (collectionUuid == new Guid("52293f4b-f98c-43ea-a80f-41047ae7ab58"))
+ {
+ Configurator.ConfigureTableParts(publication, tableParts);
+ }
+
+ ConfigureArticles(publication);
+ }
+
+ private void ConfigureArticles(Publication publication)
+ {
+ string fileName = publication.FileName.ToString() + ".1";
+ ConfigObject configObject = Configurator.FileReader.ReadConfigObject(fileName);
+ if (configObject == null) return; // not found
+
+ int count = configObject.GetInt32(new[] { 1 });
+ if (count == 0) return;
+
+ int step = 2;
+ for (int i = 1; i <= count; i++)
+ {
+ Guid uuid = configObject.GetUuid(new[] { i * step });
+ AutoPublication setting = (AutoPublication)configObject.GetInt32(new[] { (i * step) + 1 });
+ publication.Articles.Add(uuid, setting);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/EntryBase.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/EntryBase.cs
new file mode 100644
index 0000000..74648c2
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/EntryBase.cs
@@ -0,0 +1,14 @@
+namespace YPermitin.SQLCLR.YellowMetadataReader
+{
+ public abstract class EntryBase
+ {
+ ///
+ /// Строка подключения к SQL Server.
+ ///
+ /// По умолчанию используется контекстное соединение,
+ /// из под которого выполнен вызов функции или процедуры со стороны SQL Server.
+ ///
+ public static string ConnectionString { get; set; }
+ = "context connection=true";
+ }
+}
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/EntryMetadata.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/EntryMetadata.cs
new file mode 100644
index 0000000..7ad237b
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/EntryMetadata.cs
@@ -0,0 +1,423 @@
+using System;
+using System.Collections;
+using Microsoft.SqlServer.Server;
+using System.Collections.Generic;
+using System.Data.SqlClient;
+using System.Data.SqlTypes;
+using System.Data;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums;
+using YPermitin.SQLCLR.YellowMetadataReader.Services;
+using System.Linq;
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader
+{
+ public sealed class EntryMetadata : EntryBase
+ {
+ #region SqlQueries
+
+ private const string QueryDatabaseList = "select [name] from sys.databases where NOT [name] in ('master', 'tempdb', 'model', 'msdb')";
+
+ #endregion
+
+ #region Infobases
+
+ ///
+ /// Получения списка баз, которые относятся к базам платформы 1С.
+ ///
+ /// Для каждой базы отображается информация о конфигурации
+ /// и дате последнего обновления информационной базы из конфигуратора.
+ ///
+ ///
+ [SqlFunction(
+ FillRowMethodName = "GetInfobasesFillRow",
+ SystemDataAccess = SystemDataAccessKind.Read,
+ DataAccess = DataAccessKind.Read)]
+ public static IEnumerable GetInfobases()
+ {
+ // Имена баз данных, которые относятся к базам платформы 1С
+ List infobasesNames = new List();
+
+ using (SqlConnection connection = new SqlConnection(ConnectionString))
+ {
+ connection.Open();
+
+ // Получаем все имена баз данных на текущем экземпляре (кроме системных баз)
+ List allDatabaseNames = new List();
+ using (SqlCommand command = new SqlCommand(QueryDatabaseList, connection))
+ {
+ command.CommandType = CommandType.Text;
+ using (var commandResult = command.ExecuteReader())
+ {
+ while (commandResult.Read())
+ {
+ string databaseName = commandResult.GetString(0);
+ allDatabaseNames.Add(databaseName);
+ }
+ }
+ }
+
+ // Кажду базу проверяем на наличие системных таблиц
+ // 'IBVersion','Params','v8users', 'Config', 'DBSchema'.
+ // Если все таблицы присутствуют, то считаем эту базы принадлежащей платформе 1С.
+ foreach (var database in allDatabaseNames)
+ {
+ try
+ {
+ List internalTables1C = new List();
+ string queryIsInfobase1C =
+@"select
+ [name]
+from [" + database + @"].[sys].[tables]
+where [name] in ('IBVersion','Params','v8users', 'Config', 'DBSchema')";
+ using (SqlCommand command = new SqlCommand(queryIsInfobase1C, connection))
+ {
+ command.CommandType = CommandType.Text;
+ using (var commandResult = command.ExecuteReader())
+ {
+ while (commandResult.Read())
+ {
+ string internalTableName = commandResult.GetString(0);
+ internalTables1C.Add(internalTableName);
+ }
+ }
+ }
+
+ // Если были обнаружены все 5 таблиц из списка,
+ // то добавляем базу в список для анализа
+ if (internalTables1C.Count == 5)
+ {
+ infobasesNames.Add(database);
+ }
+ }
+ catch
+ {
+ // ignored
+ // Любые ошибки обработки игнорируем. Базу данных, на которой возникла ошибка,
+ // пропускаем
+ }
+ }
+ }
+
+ // Читаем информацию о информационной базе и ее конфигурации
+ List infobases = new List();
+ foreach (var infobaseName in infobasesNames)
+ {
+ try
+ {
+ IMetadataService svc = new MetadataService();
+ svc.UseConnectionString(ConnectionString);
+ svc.UseDatabaseName(infobaseName);
+ var infobase = svc.OpenInfoBase(OpenInfobaseLevel.ConfigInfoOnly);
+
+ infobases.Add(new InfobaseInfo()
+ {
+ Name = infobaseName,
+ ConfigVersion = infobase.ConfigInfo.ConfigVersion,
+ ConfigAlias = infobase.ConfigInfo.Alias,
+ ConfigName = infobase.ConfigInfo.Name,
+ ConfigUiCompatibilityMode = infobase.ConfigInfo.UiCompatibilityMode.ToString(),
+ PlatformVersion = infobase.ConfigInfo.Version.ToString()
+ });
+ }
+ catch
+ {
+ // Пропускаем без ошибок, т.к. скорее всего содержимое базы некорреткно
+ }
+ }
+
+ // Дополняем информацию датой последнего обновления
+ using (SqlConnection connection = new SqlConnection(ConnectionString))
+ {
+ connection.Open();
+
+ foreach (var infobase in infobases)
+ {
+ string queryLastUpdateDate =
+@"
+IF (EXISTS (SELECT *
+ FROM [" + infobase.Name + @"].[sys].[tables] t
+ WHERE t.[name] = 'Config'))
+BEGIN
+ SELECT
+ CASE
+ WHEN ldt.LastUpdate > '4000-01-01 00:00:00'
+ THEN DATEADD(YEAR, -2000, ldt.LastUpdate)
+ ELSE ldt.LastUpdate
+ END AS [LastUpdate]
+ FROM
+ (SELECT
+ CASE
+ WHEN MAX([Creation]) > MAX([Modified])
+ THEN MAX([Creation])
+ ELSE MAX([Modified])
+ END AS [LastUpdate]
+ FROM [" + infobase.Name + @"].[dbo].[Config] WITH(NOLOCK)) AS [ldt]
+END ELSE BEGIN
+ SELECT NULL AS [LastUpdate]
+END
+";
+ try
+ {
+ using (SqlCommand command = new SqlCommand(queryLastUpdateDate, connection))
+ {
+ command.CommandType = CommandType.Text;
+ using (var commandResult = command.ExecuteReader())
+ {
+ while (commandResult.Read())
+ {
+ DateTime? lastUpdate = commandResult.GetDateTime(0);
+ infobase.InfobaseLastUpdate = lastUpdate;
+ }
+ }
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+ }
+
+ return infobases;
+ }
+
+ public static void GetInfobasesFillRow(object source, out SqlChars infobaseName, out SqlChars configVersion,
+ out SqlChars configAlias, out SqlChars configName, out SqlChars configUiCompatibilityMode,
+ out SqlChars platformVersion, out SqlDateTime lastUpdate)
+ {
+ var sourceObject = (InfobaseInfo)source;
+ infobaseName = new SqlChars(sourceObject.Name);
+ configVersion = new SqlChars(sourceObject.ConfigVersion);
+ configAlias = new SqlChars(sourceObject.ConfigAlias);
+ configName = new SqlChars(sourceObject.ConfigName);
+ configUiCompatibilityMode = new SqlChars(sourceObject.ConfigUiCompatibilityMode);
+ platformVersion = new SqlChars(sourceObject.PlatformVersion);
+ if (sourceObject.InfobaseLastUpdate == null ||
+ sourceObject.InfobaseLastUpdate <= SqlDateTime.MinValue.Value)
+ {
+ lastUpdate = SqlDateTime.MinValue.Value;
+ }
+ else
+ {
+ lastUpdate = new SqlDateTime(sourceObject.InfobaseLastUpdate ?? SqlDateTime.MinValue.Value);
+ }
+ }
+
+ public class InfobaseInfo
+ {
+ public string Name { get; set; }
+ public string ConfigName { get; set; }
+ public string ConfigAlias { get; set; }
+ public string ConfigVersion { get; set; }
+ public string PlatformVersion { get; set; }
+ public string ConfigUiCompatibilityMode { get; set; }
+ public DateTime? InfobaseLastUpdate { get; set; }
+ }
+
+ #endregion
+
+ #region InfobaseTables
+
+ ///
+ /// Получение списка таблиц информационной базы в терминах прикладного решения
+ ///
+ ///
+ [SqlFunction(
+ FillRowMethodName = "GetInfobaseTablesFillRow",
+ SystemDataAccess = SystemDataAccessKind.Read,
+ DataAccess = DataAccessKind.Read)]
+ public static IEnumerable GetInfobaseTables(string databaseName)
+ {
+ IMetadataService svc = new MetadataService();
+ svc.UseConnectionString(ConnectionString);
+ svc.UseDatabaseName(databaseName);
+
+ var infobase = svc.OpenInfoBase();
+
+ var allObjects = infobase.AllTypes
+ .SelectMany(e => e.Value.Values)
+ .ToList();
+
+ return allObjects
+ .Union(allObjects.SelectMany(e => e.TableParts))
+ .Union(allObjects.SelectMany(e => e.NestedObjects))
+ .ToList();
+ }
+ public static void GetInfobaseTablesFillRow(object source, out SqlChars tableSQL, out SqlChars table1C)
+ {
+ var sourceObject = (ApplicationObject)source;
+ tableSQL = new SqlChars(sourceObject.TableName);
+ table1C = new SqlChars(sourceObject.MetadataName);
+ }
+
+ #endregion
+
+ #region InfobaseTablesWithFields
+
+ ///
+ /// Получение списка таблиц с полями информационной базы в терминах прикладного решения
+ ///
+ ///
+ [SqlFunction(
+ FillRowMethodName = "GetInfobaseTablesWithFieldsFillRow",
+ SystemDataAccess = SystemDataAccessKind.Read,
+ DataAccess = DataAccessKind.Read)]
+ public static IEnumerable GetInfobaseTablesWithFields(string databaseName)
+ {
+ IMetadataService svc = new MetadataService();
+ svc.UseConnectionString(ConnectionString);
+ svc.UseDatabaseName(databaseName);
+
+ var infobase = svc.OpenInfoBase();
+
+ var allObjects = infobase.AllTypes
+ .SelectMany(e => e.Value.Values)
+ .Union(
+ infobase.AllTypes
+ .SelectMany(e => e.Value.Values)
+ .SelectMany(e => e.NestedObjects))
+ .ToList();
+
+ List output = new List();
+
+ foreach (ApplicationObject objectItem in allObjects)
+ {
+ foreach (var propertyItem in objectItem.Properties)
+ {
+ output.Add(new InfobaseTableFieldInfo()
+ {
+ TableSQL = objectItem.TableName,
+ Table1C = objectItem.MetadataName,
+ FieldSQL = propertyItem.DbName,
+ Field1C = propertyItem.Name
+ });
+ }
+
+ foreach (var tablePartItem in objectItem.TableParts)
+ {
+ foreach (var propertyItemForTable in tablePartItem.Properties)
+ {
+ output.Add(new InfobaseTableFieldInfo()
+ {
+ TableSQL = tablePartItem.TableName,
+ Table1C = tablePartItem.MetadataName,
+ FieldSQL = propertyItemForTable.DbName,
+ Field1C = propertyItemForTable.Name
+ });
+ }
+ }
+ }
+
+ return output;
+ }
+
+ public static void GetInfobaseTablesWithFieldsFillRow(object source,
+ out SqlChars tableSQL, out SqlChars table1C,
+ out SqlChars fieldSQL, out SqlChars field1C)
+ {
+ var sourceObject = (InfobaseTableFieldInfo)source;
+ tableSQL = new SqlChars(sourceObject.TableSQL);
+ table1C = new SqlChars(sourceObject.Table1C);
+ fieldSQL = new SqlChars(sourceObject.FieldSQL);
+ field1C = new SqlChars(sourceObject.Field1C);
+ }
+
+ public class InfobaseTableFieldInfo
+ {
+ public string TableSQL { get; set; }
+ public string Table1C { get; set; }
+ public string FieldSQL { get; set; }
+ public string Field1C { get; set; }
+ }
+
+ #endregion
+
+ #region InfobaseEnumerations
+
+ ///
+ /// Получения списка перечислений информационной базы с их значениями в терминах прикладного решения
+ ///
+ ///
+ [SqlFunction(
+ FillRowMethodName = "GetInfobasesEnumerationsFillRow",
+ SystemDataAccess = SystemDataAccessKind.Read,
+ DataAccess = DataAccessKind.Read)]
+ public static IEnumerable GetInfobasesEnumerations(string databaseName)
+ {
+ IMetadataService svc = new MetadataService();
+ svc.UseConnectionString(ConnectionString);
+ svc.UseDatabaseName(databaseName);
+ var infobase = svc.OpenInfoBase();
+
+ var allEnumerations = infobase.Enumerations
+ .Where(e => e.Value is Enumeration)
+ .Select(e => (Enumeration)e.Value)
+ .ToList();
+
+ List output = new List();
+
+ foreach (Enumeration enumerationItem in allEnumerations)
+ {
+ foreach (var valueItem in enumerationItem.Values)
+ {
+ output.Add(new InfobaseEnumerationField()
+ {
+ TableSQL = enumerationItem.TableName,
+ Enumeration = enumerationItem.MetadataName,
+ ValueId = valueItem.Uuid.ToString(),
+ ValueOrder = valueItem.OrderNumber,
+ ValueName = valueItem.Name,
+ ValueAlias = valueItem.Alias
+ });
+ }
+ }
+
+ return output;
+ }
+
+ public static void GetInfobasesEnumerationsFillRow(object source,
+ out SqlChars tableSQL, out SqlChars table1C,
+ out SqlChars valueId, out SqlChars valueName, out SqlChars valueAlias,
+ out SqlInt32 orderNumber)
+ {
+ var sourceObject = (InfobaseEnumerationField)source;
+ tableSQL = new SqlChars(sourceObject.TableSQL);
+ table1C = new SqlChars(sourceObject.Enumeration);
+ valueId = new SqlChars(sourceObject.ValueId);
+ orderNumber = new SqlInt32(sourceObject.ValueOrder);
+ valueName = new SqlChars(sourceObject.ValueName);
+ valueAlias = new SqlChars(sourceObject.ValueAlias);
+ }
+
+ public class InfobaseEnumerationField
+ {
+ public string TableSQL { get; set; }
+ public string Enumeration { get; set; }
+ public string ValueId { get; set; }
+ public int ValueOrder { get; set; }
+ public string ValueName { get; set; }
+ public string ValueAlias { get; set; }
+ }
+
+ #endregion
+
+ #region InternalFormatData
+
+ [SqlFunction]
+ public static string ParseInternalString(byte[] data)
+ {
+ try
+ {
+ return InternalFormatReader.ParseToString(data);
+ }
+ catch (Exception e)
+ {
+ return $"Error: {e}";
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Extensions/StringExtensions.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Extensions/StringExtensions.cs
new file mode 100644
index 0000000..eab2863
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Extensions/StringExtensions.cs
@@ -0,0 +1,18 @@
+namespace YPermitin.SQLCLR.YellowMetadataReader.Extensions
+{
+ public static class StringExtensions
+ {
+ public static string RemoveFirstUnderlineSymbol(this string source)
+ {
+ if (source.Length == 0)
+ return source;
+
+ if (source[0] == '_')
+ {
+ return source.Substring(1, source.Length - 1);
+ }
+
+ return source;
+ }
+ }
+}
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Factories/IMetadataPropertyFactory.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Factories/IMetadataPropertyFactory.cs
new file mode 100644
index 0000000..1c59999
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Factories/IMetadataPropertyFactory.cs
@@ -0,0 +1,11 @@
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Factories
+{
+ public interface IMetadataPropertyFactory
+ {
+ string GetPropertyName(SqlFieldInfo field);
+ DatabaseField CreateField(SqlFieldInfo field);
+ MetadataProperty CreateProperty(ApplicationObject owner, string name, SqlFieldInfo field);
+ }
+}
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Factories/MetadataPropertyFactory.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Factories/MetadataPropertyFactory.cs
new file mode 100644
index 0000000..bc42c78
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Factories/MetadataPropertyFactory.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects;
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Factories
+{
+ public abstract class MetadataPropertyFactory : IMetadataPropertyFactory
+ {
+ public MetadataPropertyFactory()
+ {
+ // ReSharper disable once VirtualMemberCallInConstructor
+ InitializePropertyNameLookup();
+ }
+ protected abstract void InitializePropertyNameLookup();
+ protected Dictionary PropertyNameLookup { get; } = new Dictionary();
+ protected virtual string LookupPropertyName(string fieldName)
+ {
+ if (PropertyNameLookup.TryGetValue(fieldName.ToLowerInvariant(), out string propertyName))
+ {
+ return propertyName;
+ }
+ return string.Empty;
+ }
+
+ public string GetPropertyName(SqlFieldInfo field)
+ {
+ return LookupPropertyName(field.COLUMN_NAME);
+ }
+ public DatabaseField CreateField(SqlFieldInfo field)
+ {
+ return new DatabaseField()
+ {
+ Name = field.COLUMN_NAME,
+ TypeName = field.DATA_TYPE,
+ Length = field.CHARACTER_MAXIMUM_LENGTH,
+ Scale = field.NUMERIC_SCALE,
+ Precision = field.NUMERIC_PRECISION,
+ IsNullable = field.IS_NULLABLE,
+ Purpose = (field.DATA_TYPE == "timestamp"
+ || field.COLUMN_NAME == "_version"
+ || field.COLUMN_NAME == "_Version")
+ ? FieldPurpose.Version
+ : FieldPurpose.Value
+ };
+ }
+ public MetadataProperty CreateProperty(ApplicationObject owner, string name, SqlFieldInfo field)
+ {
+ MetadataProperty property = new MetadataProperty()
+ {
+ Name = name,
+ DbName = field.COLUMN_NAME,
+ FileName = Guid.Empty,
+ Purpose = PropertyPurpose.System
+ };
+ SetupPropertyType(owner, property, field);
+ return property;
+ }
+ private void SetupPropertyType(ApplicationObject owner, MetadataProperty property, SqlFieldInfo field)
+ {
+ // TODO: учесть именования типов PostgreSQL, например (mchar, mvarchar)
+
+ if (field.DATA_TYPE == "nvarchar")
+ {
+ property.PropertyType.CanBeString = true;
+ }
+ else if (field.DATA_TYPE == "numeric")
+ {
+ property.PropertyType.CanBeNumeric = true;
+ }
+ else if (field.DATA_TYPE == "timestamp")
+ {
+ property.PropertyType.IsBinary = true;
+ }
+ else if (field.DATA_TYPE == "binary")
+ {
+ if (field.CHARACTER_MAXIMUM_LENGTH == 1)
+ {
+ property.PropertyType.CanBeBoolean = true;
+ }
+ else if (field.CHARACTER_MAXIMUM_LENGTH == 16)
+ {
+ if (field.COLUMN_NAME.ToLowerInvariant().TrimStart('_') == "idrref")
+ {
+ property.PropertyType.IsUuid = true;
+ }
+ else
+ {
+ property.PropertyType.CanBeReference = true;
+ if (owner is TablePart)
+ {
+ property.PropertyType.ReferenceTypeCode = ((TablePart)owner).Owner.TypeCode;
+ }
+ else
+ {
+ property.PropertyType.ReferenceTypeCode = owner.TypeCode;
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Helpers/GeneralHelper.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Helpers/GeneralHelper.cs
new file mode 100644
index 0000000..bd836af
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Helpers/GeneralHelper.cs
@@ -0,0 +1,66 @@
+using System;
+using YPermitin.SQLCLR.YellowMetadataReader.Models;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Helpers
+{
+ public static class GeneralHelper
+ {
+ public static string GenerateConfigFullObjectKey(Guid uuid, string token, int code)
+ {
+ return $"{uuid}_{token}_{code}";
+ }
+
+ public static string GetMetadataTypeByToken(string token)
+ {
+ string metadataType;
+
+ switch (token)
+ {
+ case MetadataTokens.Reference:
+ metadataType = "Справочник";
+ break;
+ case MetadataTokens.Document:
+ metadataType = "Документ";
+ break;
+ case MetadataTokens.Const:
+ metadataType = "Константа";
+ break;
+ case MetadataTokens.Acc:
+ metadataType = "ПланСчетов";
+ break;
+ case MetadataTokens.Node:
+ metadataType = "ПланОбмена";
+ break;
+ case MetadataTokens.Enum:
+ metadataType = "Перечисление";
+ break;
+ case MetadataTokens.InfoRg:
+ metadataType = "РегистрСведений";
+ break;
+ case MetadataTokens.AccumRg:
+ metadataType = "РегистрНакопления";
+ break;
+ case MetadataTokens.AccumRgT:
+ metadataType = "РегистрНакопления";
+ break;
+ case MetadataTokens.AccumRgChngR:
+ metadataType = "РегистрНакопления";
+ break;
+ case MetadataTokens.AccumRgOpt:
+ metadataType = "РегистрНакопления";
+ break;
+ case MetadataTokens.AccRg:
+ metadataType = "РегистрБухгалтерии";
+ break;
+ case MetadataTokens.Chrc:
+ metadataType = "ПланВидовХарактеристик";
+ break;
+ default:
+ metadataType = "Unspecified";
+ break;
+ }
+
+ return metadataType;
+ }
+ }
+}
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/ApplicationObject.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/ApplicationObject.cs
new file mode 100644
index 0000000..95b007a
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/ApplicationObject.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using YPermitin.SQLCLR.YellowMetadataReader.Helpers;
+using YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Models
+{
+ ///Класс для описания объектов метаданных (справочников, документов, регистров и т.п.)
+ public class ApplicationObject : MetadataObject
+ {
+ ///Целочисленный идентификатор объекта метаданных из файла DBNames
+ public int TypeCode { get; set; }
+ public string TableName { get; set; }
+ public string Token { get; set; }
+
+ private string _fullKey;
+ public string FullKey
+ {
+ get
+ {
+ if (_fullKey == null)
+ {
+ _fullKey = GeneralHelper.GenerateConfigFullObjectKey(Uuid, Token, TypeCode);
+ }
+
+ return _fullKey;
+ }
+ }
+
+ // ReSharper disable once InconsistentNaming
+ protected string _metadataName;
+
+ public virtual string MetadataName
+ {
+ get
+ {
+ if (_metadataName == null)
+ {
+ string typeNameByToken = GeneralHelper.GetMetadataTypeByToken(Token);
+ _metadataName = $"{typeNameByToken}.{Name}";
+ }
+
+ return _metadataName;
+ }
+ }
+
+ public List Properties { get; set; } = new List();
+ public List TableParts { get; set; } = new List(); // TODO: not all of the metadata objects have table parts
+ public bool IsReferenceType
+ {
+ get
+ {
+ Type thisType = GetType();
+ return thisType == typeof(Account)
+ || thisType == typeof(Catalog)
+ || thisType == typeof(Document)
+ || thisType == typeof(Enumeration)
+ || thisType == typeof(Publication)
+ || thisType == typeof(Characteristic);
+ }
+ }
+
+ ///
+ /// Зависимые служебные таблицы
+ ///
+ public List NestedObjects { get; set; } = new List();
+
+ public override string ToString()
+ {
+ return $"{TableName}:{TypeCode}";
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/ConfigInfo.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/ConfigInfo.cs
new file mode 100644
index 0000000..9ae25a1
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/ConfigInfo.cs
@@ -0,0 +1,48 @@
+using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Models
+{
+ public sealed class ConfigInfo
+ {
+ ///
+ /// Имя конфигурации
+ ///
+ public string Name { get; set; } = string.Empty;
+ ///
+ /// Синоним конфигурации
+ ///
+ public string Alias { get; set; } = string.Empty;
+ ///
+ /// Комментарий
+ ///
+ public string Comment { get; set; } = string.Empty;
+ ///
+ /// Режим совместимости (версия платформы)
+ ///
+ public int Version { get; set; }
+ ///
+ /// Версия конфигурации
+ ///
+ public string ConfigVersion { get; set; } = string.Empty;
+ ///
+ /// Режим использования синхронных вызовов расширений платформы и внешних компонент
+ ///
+ public SyncCallsMode SyncCallsMode { get; set; }
+ ///
+ /// Режим управления блокировкой данных
+ ///
+ public DataLockingMode DataLockingMode { get; set; }
+ ///
+ /// Режим использования модальности
+ ///
+ public ModalWindowMode ModalWindowMode { get; set; }
+ ///
+ /// Режим автонумерации объектов
+ ///
+ public AutoNumberingMode AutoNumberingMode { get; set; }
+ ///
+ /// Режим совместимости интерфейса
+ ///
+ public UiCompatibilityMode UiCompatibilityMode { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/ConfigObject.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/ConfigObject.cs
new file mode 100644
index 0000000..2be2fc3
--- /dev/null
+++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/ConfigObject.cs
@@ -0,0 +1,152 @@
+using System;
+using System.Collections.Generic;
+
+namespace YPermitin.SQLCLR.YellowMetadataReader.Models
+{
+ public sealed class ConfigObject
+ {
+ public List