From 27d8200ebe708c9592b9dde14d85f8291a285f0a Mon Sep 17 00:00:00 2001 From: Permitin Yury Date: Sun, 23 Apr 2023 18:30:45 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=20SQLCLR=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D1=87=D1=82=D0=B5=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=BC=D0=B5=D1=82=D0=B0=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20?= =?UTF-8?q?=D0=BD=D0=B0=D0=BF=D1=80=D1=8F=D0=BC=D1=83=D1=8E=20=D0=B8=D0=B7?= =?UTF-8?q?=20=D0=B1=D0=B0=D0=B7=D1=8B=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B?= =?UTF-8?q?=D1=85=20=D1=81=D1=80=D0=B5=D0=B4=D1=81=D1=82=D0=B2=D0=B0=D0=BC?= =?UTF-8?q?=D0=B8=20TSQL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлено решение YellowMetadataReader - SQLCLR проект для чтения метаданных напрямую из базы данных средствами TSQL - Актуализация описания проектов раздела SQLCLR --- .../Projects/DevAdmHelpers/Readme.md | 11 +- .../Projects/YellowMetadataReader/Readme.md | 289 +++- .../YellowMetadataReader.CLI/App.config | 6 + .../YellowMetadataReader.CLI/Program.cs | 24 + .../Properties/AssemblyInfo.cs | 35 + .../YellowMetadataReader.CLI.csproj | 59 + .../YellowMetadataReader.sln | 44 + .../YellowMetadataReader.sln.DotSettings | 9 + .../Enrichers/AccountEnricher.cs | 84 + .../Enrichers/AccountingRegisterEnricher.cs | 69 + .../Enrichers/AccumulationRegisterEnricher.cs | 73 + .../AccumulationRegisterTotalEnricher.cs | 54 + .../Enrichers/CatalogEnricher.cs | 94 + .../Enrichers/CharacteristicEnricher.cs | 87 + .../Enrichers/ConstantEnricher.cs | 28 + .../Converters/DataTypeInfoConverter.cs | 149 ++ .../Converters/IConfigObjectConverter.cs | 9 + .../Enrichers/DbNamesEnricher.cs | 123 ++ .../Enrichers/DocumentEnricher.cs | 74 + .../Enrichers/EnumerationEnricher.cs | 75 + .../Enrichers/IContentEnricher.cs | 9 + .../Enrichers/InfoBaseEnricher.cs | 203 +++ .../Enrichers/InformationRegisterEnricher.cs | 73 + .../Enrichers/PublicationEnricher.cs | 85 + .../YellowMetadataReader/EntryBase.cs | 14 + .../YellowMetadataReader/EntryMetadata.cs | 423 +++++ .../Extensions/StringExtensions.cs | 18 + .../Factories/IMetadataPropertyFactory.cs | 11 + .../Factories/MetadataPropertyFactory.cs | 104 ++ .../Helpers/GeneralHelper.cs | 66 + .../Models/ApplicationObject.cs | 73 + .../YellowMetadataReader/Models/ConfigInfo.cs | 48 + .../Models/ConfigObject.cs | 152 ++ .../Models/DataTypeInfo.cs | 93 + .../Models/DatabaseField.cs | 30 + .../YellowMetadataReader/Models/DiffObject.cs | 14 + .../Models/Enums/AutoNumberingMode.cs | 17 + .../Models/Enums/CodeType.cs | 11 + .../Models/Enums/DataLockingMode.cs | 21 + .../Models/Enums/DateTimePart.cs | 9 + .../Models/Enums/FieldPurpose.cs | 28 + .../Models/Enums/HierarchyType.cs | 11 + .../Models/Enums/ModalWindowMode.cs | 21 + .../Models/Enums/NumberType.cs | 8 + .../Models/Enums/NumericKind.cs | 8 + .../Models/Enums/OpenInfobaseLevel.cs | 18 + .../Models/Enums/Periodicity.cs | 11 + .../Models/Enums/PropertyPurpose.cs | 16 + .../Models/Enums/PropertyUsage.cs | 21 + .../Models/Enums/PublicationKind.cs | 17 + .../Models/Enums/RegisterKind.cs | 17 + .../Models/Enums/RegisterPeriodicity.cs | 13 + .../Models/Enums/StringLength.cs | 8 + .../Models/Enums/SyncCallsMode.cs | 21 + .../Models/Enums/UICompatibilityMode.cs | 25 + .../Models/Enums/VersionCompatibilityMode.cs | 10 + .../YellowMetadataReader/Models/InfoBase.cs | 182 ++ .../Models/Interfaces/IAggregate.cs | 10 + .../Models/Interfaces/IDescription.cs | 7 + .../Models/Interfaces/IPredefinedValues.cs | 10 + .../Models/Interfaces/IReferenceCode.cs | 10 + .../Models/Interfaces/IReferenceHierarchy.cs | 10 + .../Models/MetaObjects/Account.cs | 30 + .../Models/MetaObjects/AccountingRegister.cs | 18 + .../MetaObjects/AccumulationRegister.cs | 32 + .../MetaObjects/AccumulationRegisterTotal.cs | 23 + .../Models/MetaObjects/Catalog.cs | 51 + .../Models/MetaObjects/Characteristic.cs | 39 + .../Models/MetaObjects/CompoundType.cs | 8 + .../Models/MetaObjects/Constant.cs | 16 + .../Models/MetaObjects/Document.cs | 24 + .../Models/MetaObjects/Enumeration.cs | 26 + .../Models/MetaObjects/InformationRegister.cs | 24 + .../Models/MetaObjects/Publication.cs | 50 + .../Models/MetaObjects/SharedProperty.cs | 22 + .../Models/MetaObjects/TablePart.cs | 34 + .../Models/MetadataObject.cs | 29 + .../Models/MetadataProperty.cs | 30 + .../Models/MetadataTokens.cs | 122 ++ .../Models/SqlFieldInfo.cs | 21 + .../Properties/AssemblyInfo.cs | 35 + .../Services/ConfigFileParser.cs | 95 + .../Services/ConfigFileReader.cs | 280 +++ .../Services/Configurator.cs | 1535 +++++++++++++++++ .../Services/IMetadataService.cs | 43 + .../Services/ISqlMetadataReader.cs | 16 + .../Services/InternalFormatReader.cs | 62 + .../Services/MetadataService.cs | 66 + .../Services/SqlMetadataReader.cs | 241 +++ .../YellowMetadataReader.csproj | 128 ++ 90 files changed, 6450 insertions(+), 2 deletions(-) create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.CLI/App.config create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.CLI/Program.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.CLI/Properties/AssemblyInfo.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.CLI/YellowMetadataReader.CLI.csproj create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.sln create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader.sln.DotSettings create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/AccountEnricher.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/AccountingRegisterEnricher.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/AccumulationRegisterEnricher.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/AccumulationRegisterTotalEnricher.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/CatalogEnricher.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/CharacteristicEnricher.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/ConstantEnricher.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/Converters/DataTypeInfoConverter.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/Converters/IConfigObjectConverter.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/DbNamesEnricher.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/DocumentEnricher.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/EnumerationEnricher.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/IContentEnricher.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/InfoBaseEnricher.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/InformationRegisterEnricher.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Enrichers/PublicationEnricher.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/EntryBase.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/EntryMetadata.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Extensions/StringExtensions.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Factories/IMetadataPropertyFactory.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Factories/MetadataPropertyFactory.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Helpers/GeneralHelper.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/ApplicationObject.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/ConfigInfo.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/ConfigObject.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/DataTypeInfo.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/DatabaseField.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/DiffObject.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/AutoNumberingMode.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/CodeType.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/DataLockingMode.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/DateTimePart.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/FieldPurpose.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/HierarchyType.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/ModalWindowMode.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/NumberType.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/NumericKind.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/OpenInfobaseLevel.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/Periodicity.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/PropertyPurpose.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/PropertyUsage.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/PublicationKind.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/RegisterKind.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/RegisterPeriodicity.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/StringLength.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/SyncCallsMode.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/UICompatibilityMode.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/VersionCompatibilityMode.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/InfoBase.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IAggregate.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IDescription.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IPredefinedValues.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IReferenceCode.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IReferenceHierarchy.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Account.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/AccountingRegister.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/AccumulationRegister.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/AccumulationRegisterTotal.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Catalog.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Characteristic.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/CompoundType.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Constant.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Document.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Enumeration.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/InformationRegister.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Publication.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/SharedProperty.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/TablePart.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetadataObject.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetadataProperty.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetadataTokens.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/SqlFieldInfo.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Properties/AssemblyInfo.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/ConfigFileParser.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/ConfigFileReader.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/Configurator.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/IMetadataService.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/ISqlMetadataReader.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/InternalFormatReader.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/MetadataService.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/SqlMetadataReader.cs create mode 100644 SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/YellowMetadataReader.csproj 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 Values { get; } = new List(); + + public int GetInt32(int[] path) + { + if (path.Length == 1) + { + return int.Parse((string)Values[path[0]]); + } + + int i = 0; + List values = Values; + do + { + values = ((ConfigObject)values[path[i]]).Values; + i++; + } + while (i < path.Length - 1); + + return int.Parse((string)values[path[i]]); + } + public Guid GetUuid(int[] path) + { + return new Guid(GetString(path)); + } + public string GetString(int[] path) + { + if (path.Length == 1) + { + return (string)Values[path[0]]; + } + + int i = 0; + List values = Values; + do + { + values = ((ConfigObject)values[path[i]]).Values; + i++; + } + while (i < path.Length - 1); + + return (string)values[path[i]]; + } + public ConfigObject GetObject(int[] path) + { + if (path.Length == 1) + { + return (ConfigObject)Values[path[0]]; + } + + int i = 0; + List values = Values; + do + { + values = ((ConfigObject)values[path[i]]).Values; + i++; + } + while (i < path.Length - 1); + + return (ConfigObject)values[path[i]]; + } + + public DiffObject CompareTo(ConfigObject target) + { + DiffObject diff = new DiffObject() + { + Path = string.Empty, + SourceValue = this, + TargetValue = target, + DiffKind = DiffKind.None + }; + + CompareObjects(this, target, diff); + + return diff; + } + private void CompareObjects(ConfigObject source, ConfigObject target, DiffObject diff) + { + int sourceCount = source.Values.Count; + int targetCount = target.Values.Count; + int count = Math.Min(sourceCount, targetCount); + + ConfigObject mdSource; + ConfigObject mdTarget; + for (int i = 0; i < count; i++) // update + { + mdSource = source.Values[i] as ConfigObject; + mdTarget = target.Values[i] as ConfigObject; + + if (mdSource != null && mdTarget != null) + { + DiffObject newDiff = CreateDiff(diff, DiffKind.Update, mdSource, mdTarget, i); + CompareObjects(mdSource, mdTarget, newDiff); + if (newDiff.DiffObjects.Count > 0) + { + diff.DiffObjects.Add(newDiff); + } + } + else if (mdSource != null || mdTarget != null) + { + diff.DiffObjects.Add( + CreateDiff( + diff, DiffKind.Update, source.Values[i], target.Values[i], i)); + } + else + { + if ((string)source.Values[i] != (string)target.Values[i]) + { + diff.DiffObjects.Add( + CreateDiff( + diff, DiffKind.Update, source.Values[i], target.Values[i], i)); + } + } + } + + if (sourceCount > targetCount) // delete + { + for (int i = count; i < sourceCount; i++) + { + diff.DiffObjects.Add( + CreateDiff( + diff, DiffKind.Delete, source.Values[i], null, i)); + } + } + else if (targetCount > sourceCount) // insert + { + for (int i = count; i < targetCount; i++) + { + diff.DiffObjects.Add( + CreateDiff( + diff, DiffKind.Insert, null, target.Values[i], i)); + } + } + } + private DiffObject CreateDiff(DiffObject parent, DiffKind kind, object source, object target, int path) + { + return new DiffObject() + { + Path = parent.Path + (string.IsNullOrEmpty(parent.Path) ? string.Empty : ".") + path.ToString(), + SourceValue = source, + TargetValue = target, + DiffKind = kind + }; + } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/DataTypeInfo.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/DataTypeInfo.cs new file mode 100644 index 0000000..3601e8e --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/DataTypeInfo.cs @@ -0,0 +1,93 @@ +// В целях оптимизации в свойстве ReferenceTypeCode класса DataTypeInfo +// не хранятся все допустимые для данного описания коды ссылочных типов. +// В случае составного типа код типа конкретного значения можно получить в базе данных в поле {имя поля}_TRef. +// В случае же сохранения кода типа в базу данных код типа можно получить из свойства ApplicationObject.TypeCode. +// У не составных типов такого поля в базе данных нет, поэтому необходимо сохранить код типа в DataTypeInfo, +// именно по этому значение свойства ReferenceTypeCode класса DataTypeInfo может быть больше ноля. + +using System; +using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models +{ + // TODO: rename class to DataTypeSet + // TODO: think on how to get set of reference types, assigned to this DataTypeSet, on demand + + ///Класс для описания типов данных свойства объекта метаданных (реквизита, измерения или ресурса) + public sealed class DataTypeInfo + { + // TODO: add internal flags field "types" so as to use bitwise operations + + ///Типом значения свойства может быть "Строка" (поддерживает составной тип данных) + public bool CanBeString { get; set; } = false; + public int StringLength { get; set; } = 10; + public StringKind StringKind { get; set; } = StringKind.Unlimited; + ///Типом значения свойства может быть "Булево" (поддерживает составной тип данных) + public bool CanBeBoolean { get; set; } = false; + ///Типом значения свойства может быть "Число" (поддерживает составной тип данных) + public bool CanBeNumeric { get; set; } = false; + public int NumericScale { get; set; } = 0; + public int NumericPrecision { get; set; } = 10; + public NumericKind NumericKind { get; set; } = NumericKind.Unsigned; + ///Типом значения свойства может быть "Дата" (поддерживает составной тип данных) + public bool CanBeDateTime { get; set; } = false; + public DateTimePart DateTimePart { get; set; } = DateTimePart.Date; + ///Типом значения свойства может быть "Ссылка" (поддерживает составной тип данных) + public bool CanBeReference { get; set; } = false; + ///Типом значения свойства является byte[8] - версия данных, timestamp, rowversion.Не поддерживает составной тип данных. + public bool IsBinary { get; set; } = false; + ///Тип значения свойства "УникальныйИдентификатор", binary(16). Не поддерживает составной тип данных. + public bool IsUuid { get; set; } = false; + ///Тип значения свойства "ХранилищеЗначения", varbinary(max). Не поддерживает составной тип данных. + public bool IsValueStorage { get; set; } = false; + ///UUID ссылочного типа значения. + public Guid ReferenceTypeUuid { get; set; } = Guid.Empty; + ///Код ссылочного типа значения. По умолчанию равен 0 - многозначный ссылочный тип (составной тип данных). + private int _referenceTypeCode; + public int ReferenceTypeCode + { + set { _referenceTypeCode = value; } + get + { + if (_referenceTypeCode == 0 && ReferenceTypeUuid != Guid.Empty) + { + // TODO: lookup type code by type uuid - it can be reference type, compound type or characteristic + } + return _referenceTypeCode; + } + } + ///Проверяет имеет ли свойство составной тип данных + public bool IsMultipleType + { + get + { + if (IsUuid || IsValueStorage || IsBinary) return false; + + int count = 0; + if (CanBeString) count++; + if (CanBeBoolean) count++; + if (CanBeNumeric) count++; + if (CanBeDateTime) count++; + if (CanBeReference) count++; + if (count > 1) return true; + + if (CanBeReference && ReferenceTypeUuid == Guid.Empty) return true; + + return false; + } + } + public override string ToString() + { + if (IsMultipleType) return "Multiple"; + else if (IsUuid) return "Uuid"; + else if (IsBinary) return "Binary"; + else if (IsValueStorage) return "ValueStorage"; + else if (CanBeString) return "String"; + else if (CanBeBoolean) return "Boolean"; + else if (CanBeNumeric) return "Numeric"; + else if (CanBeDateTime) return "DateTime"; + else if (CanBeReference) return "Reference"; + else return "Unknown"; + } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/DatabaseField.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/DatabaseField.cs new file mode 100644 index 0000000..c0015dd --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/DatabaseField.cs @@ -0,0 +1,30 @@ +using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models +{ + public sealed class DatabaseField + { + public DatabaseField() { } + public DatabaseField(string name, string typeName, int length) + { + Name = name; + Length = length; + TypeName = typeName; + } + public DatabaseField(string name, string typeName, int length, int precision, int scale) : this(name, typeName, length) + { + Scale = scale; + Precision = precision; + } + public string Name { get; set; } + public FieldPurpose Purpose { get; set; } = FieldPurpose.Value; + public string TypeName { get; set; } + public int Length { get; set; } + public int Precision { get; set; } + public int Scale { get; set; } + public bool IsNullable { get; set; } + public int KeyOrdinal { get; set; } + public bool IsPrimaryKey { get; set; } + public override string ToString() { return Name; } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/DiffObject.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/DiffObject.cs new file mode 100644 index 0000000..c1f9d85 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/DiffObject.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models +{ + public enum DiffKind { None, Insert, Update, Delete } + public sealed class DiffObject + { + public string Path { get; set; } + public DiffKind DiffKind { get; set; } + public object SourceValue { get; set; } + public object TargetValue { get; set; } + public List DiffObjects { get; } = new List(); + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/AutoNumberingMode.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/AutoNumberingMode.cs new file mode 100644 index 0000000..c1dba24 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/AutoNumberingMode.cs @@ -0,0 +1,17 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + /// + /// Режим автонумерации объектов + /// + public enum AutoNumberingMode + { + /// + /// Освобождать автоматически + /// + FreeAutomatically = 0, + /// + /// Не освобождать автоматически + /// + DoNotFreeAutomatically = 1 + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/CodeType.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/CodeType.cs new file mode 100644 index 0000000..762de19 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/CodeType.cs @@ -0,0 +1,11 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + /// + /// Тип данных кода справочника или номера документа + /// + public enum CodeType + { + Number = 0, + String = 1 + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/DataLockingMode.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/DataLockingMode.cs new file mode 100644 index 0000000..8c5d3e6 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/DataLockingMode.cs @@ -0,0 +1,21 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + /// + /// Режим управления блокировкой данных + /// + public enum DataLockingMode + { + /// + /// Автоматический + /// + Automatic = 0, + /// + /// Управляемый + /// + Managed = 1, + /// + /// Автоматический и управляемый + /// + AutomaticAndManaged = 2 + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/DateTimePart.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/DateTimePart.cs new file mode 100644 index 0000000..5e16840 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/DateTimePart.cs @@ -0,0 +1,9 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + public enum DateTimePart + { + Date, + Time, + DateTime + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/FieldPurpose.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/FieldPurpose.cs new file mode 100644 index 0000000..8f83420 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/FieldPurpose.cs @@ -0,0 +1,28 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + public enum FieldPurpose + { + /// Value of the property (default). + Value, + /// Helps to locate fields having [boolean, string, number, binary, datetime, object] types + Discriminator, + /// Boolean value. + Boolean, + /// String value. + String, + /// Numeric value. + Numeric, + /// Binary value (bytes array). + Binary, + /// Date and time value. + DateTime, + /// Reference type primary key value. + Object, + /// Type code of the reference type (class discriminator). + TypeCode, + /// Record's version (timestamp|rowversion). + Version, + /// Ordinal key value for ordered sets of records. + Ordinal + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/HierarchyType.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/HierarchyType.cs new file mode 100644 index 0000000..d2230af --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/HierarchyType.cs @@ -0,0 +1,11 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + /// + /// Тип иерархии справочника + /// + public enum HierarchyType + { + Groups = 0, + Elements = 1 + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/ModalWindowMode.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/ModalWindowMode.cs new file mode 100644 index 0000000..ba6a74e --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/ModalWindowMode.cs @@ -0,0 +1,21 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + /// + /// Режим использования модальности + /// + public enum ModalWindowMode + { + /// + /// Использовать + /// + Use = 0, + /// + /// Использовать с предупреждениями + /// + UseWithAlert = 1, + /// + /// Не использовать + /// + DoNotUse = 2 + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/NumberType.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/NumberType.cs new file mode 100644 index 0000000..a143e1a --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/NumberType.cs @@ -0,0 +1,8 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + public enum NumberType + { + Number = 0, + String = 1 + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/NumericKind.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/NumericKind.cs new file mode 100644 index 0000000..d184247 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/NumericKind.cs @@ -0,0 +1,8 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + public enum NumericKind + { + Signed = 1, + Unsigned = 0 + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/OpenInfobaseLevel.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/OpenInfobaseLevel.cs new file mode 100644 index 0000000..5795e3c --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/OpenInfobaseLevel.cs @@ -0,0 +1,18 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + /// + /// Уровень чтения информации о метаданных + /// + public enum OpenInfobaseLevel + { + /// + /// Только базовая информация о конфигурации + /// + ConfigInfoOnly = 0, + + /// + /// Полная информация о конфигурации + /// + ConfigFull = 1 + } +} diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/Periodicity.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/Periodicity.cs new file mode 100644 index 0000000..0d0e1f2 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/Periodicity.cs @@ -0,0 +1,11 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + public enum Periodicity + { + None = 0, + Year = 1, + Quarter = 2, + Month = 3, + Day = 4 + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/PropertyPurpose.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/PropertyPurpose.cs new file mode 100644 index 0000000..7919913 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/PropertyPurpose.cs @@ -0,0 +1,16 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + public enum PropertyPurpose + { + /// The property is being used by system. + System, + /// The property is being used as a property. + Property, + /// The property is being used as a dimension. + Dimension, + /// The property is being used as a measure. + Measure, + /// This property is used to reference parent (adjacency list). + Hierarchy + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/PropertyUsage.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/PropertyUsage.cs new file mode 100644 index 0000000..048e7fa --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/PropertyUsage.cs @@ -0,0 +1,21 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + /// + /// Вариант использования реквизита справочника или плана видов характеристик для групп и элементов + /// + public enum PropertyUsage + { + /// + /// Использовать реквизит только для элементов + /// + Item = 0, + /// + /// Использовать реквизит только для групп + /// + Folder = 1, + /// + /// Использовать реквизит для элементов и групп + /// + ItemAndFolder = 2 + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/PublicationKind.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/PublicationKind.cs new file mode 100644 index 0000000..b911282 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/PublicationKind.cs @@ -0,0 +1,17 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + /// + /// Авторегистрация объекта в плане обмена + /// + public enum AutoPublication + { + /// + /// Запретить + /// + Forbid = 0, + /// + /// Разрешить + /// + Allow = 1 + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/RegisterKind.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/RegisterKind.cs new file mode 100644 index 0000000..d973963 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/RegisterKind.cs @@ -0,0 +1,17 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + /// + /// Вид регистра накопления + /// + public enum RegisterKind + { + /// + /// Остатки + /// + Balance = 0, + /// + /// Обороты + /// + Turnovers = 1 + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/RegisterPeriodicity.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/RegisterPeriodicity.cs new file mode 100644 index 0000000..735cefc --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/RegisterPeriodicity.cs @@ -0,0 +1,13 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + public enum RegisterPeriodicity + { + None = 0, + Year = 1, + Quarter = 2, + Month = 3, + Day = 4, + Second = 5, + Recorder = 6 // Регистр сведений, подчинённый регистратору, с периодичностью по позиции регистратора + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/StringLength.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/StringLength.cs new file mode 100644 index 0000000..724d235 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/StringLength.cs @@ -0,0 +1,8 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + public enum StringKind + { + Fixed = 0, + Unlimited = 1 + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/SyncCallsMode.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/SyncCallsMode.cs new file mode 100644 index 0000000..c2d09c8 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/SyncCallsMode.cs @@ -0,0 +1,21 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + /// + /// Режим использования синхронных вызовов расширений платформы и внешних компонент + /// + public enum SyncCallsMode + { + /// + /// Использовать + /// + Use = 0, + /// + /// Использовать с предупреждениями + /// + UseWithAlert = 1, + /// + /// Не использовать + /// + DoNotUse = 2 + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/UICompatibilityMode.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/UICompatibilityMode.cs new file mode 100644 index 0000000..0f9ad0f --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/UICompatibilityMode.cs @@ -0,0 +1,25 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + /// + /// Режим совместимости интерфейса + /// + public enum UiCompatibilityMode + { + /// + /// Версия 8.2 + /// + Version82 = 0, + /// + /// Версия 8.2. Разрешить Такси + /// + Version82AllowTaxi = 1, + /// + /// Такси. Разрешить Версия 8.2 + /// + TaxiAllowVersion82 = 2, + /// + /// Такси + /// + Taxi = 3, + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/VersionCompatibilityMode.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/VersionCompatibilityMode.cs new file mode 100644 index 0000000..888d2bf --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Enums/VersionCompatibilityMode.cs @@ -0,0 +1,10 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Enums +{ + /// + /// Режим совместимости + /// + public enum VersionCompatibilityMode + { + + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/InfoBase.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/InfoBase.cs new file mode 100644 index 0000000..e979e1b --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/InfoBase.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models +{ + public class InfoBase : MetadataObject + { + public ConfigInfo ConfigInfo { get; set; } + public int YearOffset { get; set; } + public int PlatformRequiredVersion { get; set; } + public List> Registers { get; private set; } + public List> ValueTypes { get; private set; } + public List> ReferenceTypes { get; private set; } + public Dictionary> AllTypes { get; private set; } + public Dictionary UnspecifiedObjects { get; } = new Dictionary(); + public InfoBase() + { + Registers = new List>() + { + AccountingRegisters, + InformationRegisters, + AccumulationRegisters + }; + ValueTypes = new List>() + { + Constants, + AccountingRegisters, + InformationRegisters, + AccumulationRegisters + }; + ReferenceTypes = new List>() + { + Accounts, + Catalogs, + Documents, + Enumerations, + Publications, + Characteristics + }; + AllTypes = new Dictionary>() + { + { typeof(Account), Accounts }, + { typeof(AccountingRegister), AccountingRegisters }, + { typeof(AccumulationRegister), AccumulationRegisters }, + { typeof(Catalog), Catalogs }, + { typeof(Characteristic), Characteristics }, + { typeof(Constant), Constants }, + { typeof(Document), Documents }, + { typeof(Enumeration), Enumerations }, + { typeof(InformationRegister), InformationRegisters }, + { typeof(Publication), Publications }, + { typeof(ApplicationObject), UnspecifiedObjects } + }; + } + + /// + /// Коллекция всех объектов по ID + /// + public Dictionary AllObjectsById { get; } = new Dictionary(); + + ///Соответствие идентификаторов объектов метаданных типа "ТабличнаяЧасть" + public Dictionary TableParts { get; } = new Dictionary(); + public Dictionary TablePartsById { get; } = new Dictionary(); + ///Соответствие идентификаторов объектов метаданных типа "Реквизит", "Измерение", "Ресурс" + public Dictionary Properties { get; } = new Dictionary(); + public Dictionary PropertiesById { get; } = new Dictionary(); + ///Коллекция общих свойств конфигурации + public Dictionary SharedProperties { get; set; } = new Dictionary(); + ///Коллекция определяемых типов конфигурации + public Dictionary CompoundTypes { get; set; } = new Dictionary(); + ///Коллекция типов определяемых характеристиками + public Dictionary CharacteristicTypes { get; set; } = new Dictionary(); + ///Соответствие идентификаторов объектов метаданных ссылочного типа + public ConcurrentDictionary ReferenceTypeUuids { get; } = new ConcurrentDictionary(); + ///Соответствие кодов типов объектов метаданных ссылочного типа + public ConcurrentDictionary ReferenceTypeCodes { get; } = new ConcurrentDictionary(); + + #region "Коллекции ссылочных типов данных (Guid - имя файла объекта метаданных в таблице Config)" + + ///Коллекция планов счетов (ссылочный тип данных) + public Dictionary Accounts { get; } = new Dictionary(); + ///Коллекция справочников (ссылочный тип данных) + public Dictionary Catalogs { get; } = new Dictionary(); + ///Коллекция документов (ссылочный тип данных) + public Dictionary Documents { get; } = new Dictionary(); + ///Коллекция перечислений (ссылочный тип данных) + public Dictionary Enumerations { get; } = new Dictionary(); + ///Коллекция планов обмена (ссылочный тип данных) + public Dictionary Publications { get; } = new Dictionary(); + ///Коллекция планов видов характеристик (ссылочный тип данных) + public Dictionary Characteristics { get; } = new Dictionary(); + + #endregion + + #region "Коллекции значимых типов данных (Guid - имя файла объекта метаданных в таблице Config)" + + ///Коллекция констант (значимый тип данных) + public Dictionary Constants { get; } = new Dictionary(); + ///Коллекция регистров бухгалтерии (значимый тип данных) + public Dictionary AccountingRegisters { get; } = new Dictionary(); + ///Коллекция регистров сведений (значимый тип данных) + public Dictionary InformationRegisters { get; } = new Dictionary(); + ///Коллекция регистров накопления (значимый тип данных) + public Dictionary AccumulationRegisters { get; } = new Dictionary(); + + #endregion + + public void ApplyCompoundType(DataTypeInfo typeInfo, CompoundType compound) + { + // TODO: add internal flags field to the DataTypeInfo class so as to use bitwise operations + if (!typeInfo.CanBeString && compound.TypeInfo.CanBeString) typeInfo.CanBeString = true; + if (!typeInfo.CanBeBoolean && compound.TypeInfo.CanBeBoolean) typeInfo.CanBeBoolean = true; + if (!typeInfo.CanBeNumeric && compound.TypeInfo.CanBeNumeric) typeInfo.CanBeNumeric = true; + if (!typeInfo.CanBeDateTime && compound.TypeInfo.CanBeDateTime) typeInfo.CanBeDateTime = true; + if (!typeInfo.CanBeReference && compound.TypeInfo.CanBeReference) typeInfo.CanBeReference = true; + if (!typeInfo.IsUuid && compound.TypeInfo.IsUuid) typeInfo.IsUuid = true; + if (!typeInfo.IsValueStorage && compound.TypeInfo.IsValueStorage) typeInfo.IsValueStorage = true; + if (!typeInfo.IsBinary && compound.TypeInfo.IsBinary) typeInfo.IsBinary = true; + } + public void ApplyCharacteristic(DataTypeInfo typeInfo, Characteristic characteristic) + { + // TODO: add internal flags field to the DataTypeInfo class so as to use bitwise operations + if (!typeInfo.CanBeString && characteristic.TypeInfo.CanBeString) typeInfo.CanBeString = true; + if (!typeInfo.CanBeBoolean && characteristic.TypeInfo.CanBeBoolean) typeInfo.CanBeBoolean = true; + if (!typeInfo.CanBeNumeric && characteristic.TypeInfo.CanBeNumeric) typeInfo.CanBeNumeric = true; + if (!typeInfo.CanBeDateTime && characteristic.TypeInfo.CanBeDateTime) typeInfo.CanBeDateTime = true; + if (!typeInfo.CanBeReference && characteristic.TypeInfo.CanBeReference) typeInfo.CanBeReference = true; + if (!typeInfo.IsUuid && characteristic.TypeInfo.IsUuid) typeInfo.IsUuid = true; + if (!typeInfo.IsValueStorage && characteristic.TypeInfo.IsValueStorage) typeInfo.IsValueStorage = true; + if (!typeInfo.IsBinary && characteristic.TypeInfo.IsBinary) typeInfo.IsBinary = true; + } + + ///Функция возвращает объект метаданных по его полному имени или null, если не найден. + ///Полное имя объекта метаданных, например, "Справочник.Номенклатура" или "Документ.ЗаказКлиента.Товары". + public ApplicationObject GetApplicationObjectByName(string metadataName) + { + string[] names = metadataName.Split('.'); + + string typeName = names[0]; + string objectName = names[1]; + string tablePartName = null; + if (names.Length == 3) + { + tablePartName = names[2]; + } + + ApplicationObject metaObject; + Dictionary collection = null; + + if (typeName == "Справочник") collection = Catalogs; + else if (typeName == "Документ") collection = Documents; + else if (typeName == "Константа") collection = Constants; + else if (typeName == "ПланСчетов") collection = Accounts; + else if (typeName == "ПланОбмена") collection = Publications; + else if (typeName == "Перечисление") collection = Enumerations; + else if (typeName == "РегистрСведений") collection = InformationRegisters; + else if (typeName == "РегистрНакопления") collection = AccumulationRegisters; + else if (typeName == "РегистрБухгалтерии") collection = AccountingRegisters; + else if (typeName == "ПланВидовХарактеристик") collection = Characteristics; + if (collection == null) + { + return null; + } + + metaObject = collection.Values.Where(o => o.Name == objectName).FirstOrDefault(); + if (metaObject == null) + { + return null; + } + + if (tablePartName != null) + { + return metaObject.TableParts.Where(t => t.Name == tablePartName).FirstOrDefault(); + } + + return metaObject; + } + } +} diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IAggregate.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IAggregate.cs new file mode 100644 index 0000000..a8eb553 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IAggregate.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Interfaces +{ + public interface IAggregate // only reference types can be aggregates + { + ApplicationObject Owner { get; set; } + List Elements { get; set; } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IDescription.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IDescription.cs new file mode 100644 index 0000000..b579616 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IDescription.cs @@ -0,0 +1,7 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Interfaces +{ + public interface IDescription + { + int DescriptionLength { get; set; } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IPredefinedValues.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IPredefinedValues.cs new file mode 100644 index 0000000..7ca5d62 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IPredefinedValues.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Interfaces +{ + public interface IPredefinedValues + { + List PredefinedValues { get; set; } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IReferenceCode.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IReferenceCode.cs new file mode 100644 index 0000000..0af8a74 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IReferenceCode.cs @@ -0,0 +1,10 @@ +using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Interfaces +{ + public interface IReferenceCode + { + int CodeLength { get; set; } + CodeType CodeType { get; set; } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IReferenceHierarchy.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IReferenceHierarchy.cs new file mode 100644 index 0000000..c6fd1ea --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/Interfaces/IReferenceHierarchy.cs @@ -0,0 +1,10 @@ +using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.Interfaces +{ + public interface IReferenceHierarchy + { + bool IsHierarchical { get; set; } + HierarchyType HierarchyType { get; set; } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Account.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Account.cs new file mode 100644 index 0000000..7ef2b90 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Account.cs @@ -0,0 +1,30 @@ +using YPermitin.SQLCLR.YellowMetadataReader.Factories; +using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums; +using YPermitin.SQLCLR.YellowMetadataReader.Models.Interfaces; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects +{ + public sealed class Account : ApplicationObject, IReferenceCode, IDescription + { + //public List TableParts { get; set; } = new List(); + public int CodeLength { get; set; } + public CodeType CodeType { get; set; } + public int DescriptionLength { get; set; } + } + public sealed class AccountPropertyFactory : MetadataPropertyFactory + { + protected override void InitializePropertyNameLookup() + { + PropertyNameLookup.Add("_idrref", "Ссылка"); + PropertyNameLookup.Add("_version", "ВерсияДанных"); + PropertyNameLookup.Add("_marked", "ПометкаУдаления"); + PropertyNameLookup.Add("_predefinedid", "Предопределённый"); + PropertyNameLookup.Add("_parentidrref", "Родитель"); + PropertyNameLookup.Add("_code", "Код"); + PropertyNameLookup.Add("_description", "Наименование"); + PropertyNameLookup.Add("_orderfield", "Порядок"); + PropertyNameLookup.Add("_kind", "Тип"); + PropertyNameLookup.Add("_offbalance", "Забалансовый"); + } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/AccountingRegister.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/AccountingRegister.cs new file mode 100644 index 0000000..316d2f5 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/AccountingRegister.cs @@ -0,0 +1,18 @@ +using YPermitin.SQLCLR.YellowMetadataReader.Factories; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects +{ + public sealed class AccountingRegister : ApplicationObject + { + + } + public sealed class AccountingRegisterPropertyFactory : MetadataPropertyFactory + { + protected override void InitializePropertyNameLookup() + { + PropertyNameLookup.Add("_period", "Период"); + PropertyNameLookup.Add("_recorder", "Регистратор"); + // TODO: добавить остальные свойства + } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/AccumulationRegister.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/AccumulationRegister.cs new file mode 100644 index 0000000..dc90511 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/AccumulationRegister.cs @@ -0,0 +1,32 @@ +using YPermitin.SQLCLR.YellowMetadataReader.Factories; +using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects +{ + /// + /// Регистр накопления + /// + public sealed class AccumulationRegister : ApplicationObject + { + /// + /// Разрешить разделение итогов + /// + public bool UseSplitter { get; set; } = true; + /// + /// Вид регистра накопления (остатки, обороты) + /// + public RegisterKind RegisterKind { get; set; } = RegisterKind.Balance; + } + public sealed class AccumulationRegisterPropertyFactory : MetadataPropertyFactory + { + protected override void InitializePropertyNameLookup() + { + PropertyNameLookup.Add("_period", "Период"); // datetime2 + PropertyNameLookup.Add("_recorder", "Регистратор"); // _RecorderRRef binary(16) | _RecorderTRef binary(4) + _RecorderRRef binary(16) + PropertyNameLookup.Add("_lineno", "НомерЗаписи"); // НомерЗаписи numeric(9,0) + PropertyNameLookup.Add("_active", "Активность"); // binary(1) + PropertyNameLookup.Add("_recordkind", "ВидДвижения"); // numeric(1,0) - только регистры остатков + // _Splitter numeric(10,0) not null - разделитель итогов _AccumRgT85 _AccumRgTn91 + } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/AccumulationRegisterTotal.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/AccumulationRegisterTotal.cs new file mode 100644 index 0000000..f0ac077 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/AccumulationRegisterTotal.cs @@ -0,0 +1,23 @@ +using YPermitin.SQLCLR.YellowMetadataReader.Factories; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects +{ + /// + /// Регистр накопления (таблица итогов) + /// + public sealed class AccumulationRegisterTotal : ApplicationObject + { + } + public sealed class AccumulationRegisterTotalPropertyFactory : MetadataPropertyFactory + { + protected override void InitializePropertyNameLookup() + { + PropertyNameLookup.Add("_period", "Период"); // datetime2 + PropertyNameLookup.Add("_recorder", "Регистратор"); // _RecorderRRef binary(16) | _RecorderTRef binary(4) + _RecorderRRef binary(16) + PropertyNameLookup.Add("_lineno", "НомерЗаписи"); // НомерЗаписи numeric(9,0) + PropertyNameLookup.Add("_active", "Активность"); // binary(1) + PropertyNameLookup.Add("_recordkind", "ВидДвижения"); // numeric(1,0) - только регистры остатков + // _Splitter numeric(10,0) not null - разделитель итогов _AccumRgT85 _AccumRgTn91 + } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Catalog.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Catalog.cs new file mode 100644 index 0000000..7cc0dd1 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Catalog.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using YPermitin.SQLCLR.YellowMetadataReader.Factories; +using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums; +using YPermitin.SQLCLR.YellowMetadataReader.Models.Interfaces; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects +{ + public sealed class Catalog : ApplicationObject, IReferenceCode, IDescription, IReferenceHierarchy, IPredefinedValues + { + public int Owners { get; set; } + public int CodeLength { get; set; } = 9; + public CodeType CodeType { get; set; } = CodeType.String; + public int DescriptionLength { get; set; } = 25; + public bool IsHierarchical { get; set; } = false; + public HierarchyType HierarchyType { get; set; } = HierarchyType.Groups; + public List PredefinedValues { get; set; } = new List(); + } + public sealed class PredefinedValue + { + public Guid Uuid { get; set; } = Guid.Empty; + public string Name { get; set; } = string.Empty; + public string Code { get; set; } = string.Empty; + public bool IsFolder { get; set; } = false; + public string Description { get; set; } = string.Empty; + public List Children { get; set; } = new List(); + } + public sealed class CatalogPropertyFactory : MetadataPropertyFactory + { + protected override void InitializePropertyNameLookup() + { + PropertyNameLookup.Add("_idrref", "Ссылка"); + PropertyNameLookup.Add("_version", "ВерсияДанных"); + PropertyNameLookup.Add("_marked", "ПометкаУдаления"); + PropertyNameLookup.Add("_predefinedid", "Предопределённый"); + PropertyNameLookup.Add("_code", "Код"); // 1.17 - длина кода (0 - не используется), 1.18 - тип кода (0 - число, 1 - строка) + PropertyNameLookup.Add("_description", "Наименование"); // 1.19 - длина наименования (0 - не используется) + PropertyNameLookup.Add("_folder", "ЭтоГруппа"); // 1.36 (0 - иерархия групп и элементов, 1 - иерархия элементов) + PropertyNameLookup.Add("_parentidrref", "Родитель"); // 1.37 - иерархический (0 - нет, 1 - да) + PropertyNameLookup.Add("_owneridrref", "Владелец"); // 1.12.1 - количество владельцев справочника + PropertyNameLookup.Add("_ownerid_type", "Владелец"); // 1.12.2, 1.12.3, 1.12.N - описание владельцев + PropertyNameLookup.Add("_ownerid_rtref", "Владелец"); // 1.12.2.2.1, 1.12.3.2.1, 1.12.N.2.1 - uuid'ы владельцев + PropertyNameLookup.Add("_ownerid_rrref", "Владелец"); + // Свойство "Владелец" (1.12.1 == 0) не используется + // _OwnerIDRRef binary(16) (1.12.1 == 1) + // _OwnerID_TYPE binary(1) (1.12.1 > 1) + // _OwnerID_RTRef binary(4) (1.12.1 > 1) + // _OwnerID_RRRef binary(16) (1.12.1 > 1) + } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Characteristic.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Characteristic.cs new file mode 100644 index 0000000..a632606 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Characteristic.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using YPermitin.SQLCLR.YellowMetadataReader.Factories; +using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums; +using YPermitin.SQLCLR.YellowMetadataReader.Models.Interfaces; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects +{ + public sealed class Characteristic : ApplicationObject, IReferenceCode, IDescription, IReferenceHierarchy, IPredefinedValues + { + /// + ///Идентификатор характеристики, описания типов значений. + ///Используется как тип значения "Характеристика" в реквизитах других объектов метаданных. + /// + public Guid TypeUuid { get; set; } = Guid.Empty; + public DataTypeInfo TypeInfo { get; set; } + public int CodeLength { get; set; } = 9; + public CodeType CodeType { get; set; } = CodeType.String; + public int DescriptionLength { get; set; } = 25; + public bool IsHierarchical { get; set; } = false; + public HierarchyType HierarchyType { get; set; } = HierarchyType.Groups; + public List PredefinedValues { get; set; } = new List(); + } + public sealed class CharacteristicPropertyFactory : MetadataPropertyFactory + { + protected override void InitializePropertyNameLookup() + { + PropertyNameLookup.Add("_idrref", "Ссылка"); + PropertyNameLookup.Add("_version", "ВерсияДанных"); + PropertyNameLookup.Add("_marked", "ПометкаУдаления"); + PropertyNameLookup.Add("_predefinedid", "Предопределённый"); + PropertyNameLookup.Add("_parentidrref", "Родитель"); // необязательный + PropertyNameLookup.Add("_folder", "ЭтоГруппа"); // необязательный + PropertyNameLookup.Add("_code", "Код"); // необязательный + PropertyNameLookup.Add("_description", "Наименование"); // необязательный + PropertyNameLookup.Add("_type", "ТипЗначения"); + } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/CompoundType.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/CompoundType.cs new file mode 100644 index 0000000..00e107e --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/CompoundType.cs @@ -0,0 +1,8 @@ +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects +{ + // TODO: rename class to NamedDataTypeSet + public sealed class CompoundType : MetadataObject + { + public DataTypeInfo TypeInfo { get; set; } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Constant.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Constant.cs new file mode 100644 index 0000000..7033e55 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Constant.cs @@ -0,0 +1,16 @@ +using YPermitin.SQLCLR.YellowMetadataReader.Factories; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects +{ + public sealed class Constant : ApplicationObject + { + + } + public sealed class ConstantPropertyFactory : MetadataPropertyFactory + { + protected override void InitializePropertyNameLookup() + { + PropertyNameLookup.Add("_recordkey", "КлючЗаписи"); // binary(1) + } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Document.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Document.cs new file mode 100644 index 0000000..a3360c1 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Document.cs @@ -0,0 +1,24 @@ +using YPermitin.SQLCLR.YellowMetadataReader.Factories; +using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects +{ + public sealed class Document : ApplicationObject + { + public int NumberLength { get; set; } = 8; + public NumberType NumberType { get; set; } = NumberType.String; + public Periodicity Periodicity { get; set; } = Periodicity.None; + } + public sealed class DocumentPropertyFactory : MetadataPropertyFactory + { + protected override void InitializePropertyNameLookup() + { + PropertyNameLookup.Add("_idrref", "Ссылка"); + PropertyNameLookup.Add("_version", "ВерсияДанных"); + PropertyNameLookup.Add("_marked", "ПометкаУдаления"); + PropertyNameLookup.Add("_date_time", "Дата"); + PropertyNameLookup.Add("_number", "Номер"); // необязательный + PropertyNameLookup.Add("_posted", "Проведён"); + } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Enumeration.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Enumeration.cs new file mode 100644 index 0000000..f905b8c --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Enumeration.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using YPermitin.SQLCLR.YellowMetadataReader.Factories; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects +{ + public sealed class Enumeration : ApplicationObject + { + public List Values { get; set; } = new List(); + } + public sealed class EnumValue + { + public Guid Uuid { get; set; } = Guid.Empty; + public string Name { get; set; } = string.Empty; + public string Alias { get; set; } = string.Empty; + public int OrderNumber { get; set; } = 0; + } + public sealed class EnumerationPropertyFactory : MetadataPropertyFactory + { + protected override void InitializePropertyNameLookup() + { + PropertyNameLookup.Add("_idrref", "Ссылка"); + PropertyNameLookup.Add("_enumorder", "Порядок"); + } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/InformationRegister.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/InformationRegister.cs new file mode 100644 index 0000000..0889dd6 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/InformationRegister.cs @@ -0,0 +1,24 @@ +using YPermitin.SQLCLR.YellowMetadataReader.Factories; +using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects +{ + public sealed class InformationRegister : ApplicationObject + { + public bool UseRecorder { get; set; } + public RegisterPeriodicity Periodicity { get; set; } = RegisterPeriodicity.None; + } + public sealed class InformationRegisterPropertyFactory : MetadataPropertyFactory + { + protected override void InitializePropertyNameLookup() + { + // периодический регистр сведений + PropertyNameLookup.Add("_period", "Период"); // необязательный datetime2 + // подчинённый регистратору + PropertyNameLookup.Add("_recorderrref", "Регистратор"); // необязательный binary(16) + PropertyNameLookup.Add("_recordertref", "Регистратор"); // необязательный binary(4) + PropertyNameLookup.Add("_lineno", "НомерЗаписи"); // необязательный numeric(9,0) + PropertyNameLookup.Add("_active", "Активность"); // необязательный binary(1) + } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Publication.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Publication.cs new file mode 100644 index 0000000..90672f3 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/Publication.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using YPermitin.SQLCLR.YellowMetadataReader.Factories; +using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums; +using YPermitin.SQLCLR.YellowMetadataReader.Models.Interfaces; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects +{ + public sealed class PublicationPropertyFactory : MetadataPropertyFactory + { + protected override void InitializePropertyNameLookup() + { + // все реквизиты обязательные + PropertyNameLookup.Add("_idrref", "Ссылка"); + PropertyNameLookup.Add("_marked", "ПометкаУдаления"); + PropertyNameLookup.Add("_version", "ВерсияДанных"); + PropertyNameLookup.Add("_predefinedid", "Предопределённый"); + PropertyNameLookup.Add("_code", "Код"); + PropertyNameLookup.Add("_description", "Наименование"); + PropertyNameLookup.Add("_sentno", "НомерОтправленного"); + PropertyNameLookup.Add("_receivedno", "НомерПринятого"); + } + } + public sealed class Publication : ApplicationObject, IReferenceCode, IDescription + { + public int CodeLength { get; set; } = 9; // min 1 + public CodeType CodeType { get; set; } = CodeType.String; // always + public int DescriptionLength { get; set; } = 25; // min 1 + public bool IsDistributed { get; set; } + public Publisher Publisher { get; set; } + public List Subscribers { get; set; } = new List(); + /// + /// Состав плана обмена. Ключ словаря - идентификатор файла объекта метаданных (). + /// + public Dictionary Articles { get; set; } = new Dictionary(); + } + public sealed class Publisher + { + public Guid Uuid { get; set; } = Guid.Empty; + public string Code { get; set; } = string.Empty; + public string Name { get; set; } = string.Empty; + } + public sealed class Subscriber + { + public Guid Uuid { get; set; } = Guid.Empty; + public string Code { get; set; } = string.Empty; + public string Name { get; set; } = string.Empty; + public bool IsMarkedForDeletion { get; set; } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/SharedProperty.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/SharedProperty.cs new file mode 100644 index 0000000..88927c4 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/SharedProperty.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects +{ + public enum AutomaticUsage + { + Use = 0, + DoNotUse = 1 + } + public enum SharedPropertyUsage + { + Auto = 0, + Use = 1, + DoNotUse = 2 + } + public sealed class SharedProperty : MetadataProperty + { + public AutomaticUsage AutomaticUsage { get; set; } + public Dictionary UsageSettings { get; } = new Dictionary(); + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/TablePart.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/TablePart.cs new file mode 100644 index 0000000..649d161 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetaObjects/TablePart.cs @@ -0,0 +1,34 @@ +using YPermitin.SQLCLR.YellowMetadataReader.Factories; +using YPermitin.SQLCLR.YellowMetadataReader.Helpers; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects +{ + public sealed class TablePart : ApplicationObject + { + public ApplicationObject Owner { get; set; } + + public override string MetadataName + { + get + { + if (_metadataName == null) + { + string typeNameByToken = GeneralHelper.GetMetadataTypeByToken(Owner.Token); + _metadataName = $"{typeNameByToken}.{Owner.Name}.ТабличнаяЧасть.{Name}"; + } + + return _metadataName; + } + } + } + public sealed class TablePartPropertyFactory : MetadataPropertyFactory + { + protected override void InitializePropertyNameLookup() + { + // все реквизиты обязательные + PropertyNameLookup.Add("_idrref", "Ссылка"); // _Reference31_IDRRef binary(16) + PropertyNameLookup.Add("_keyfield", "Ключ"); // binary(4) + PropertyNameLookup.Add("_lineno", "НомерСтроки"); // _LineNo49 numeric(5,0) - DBNames + } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetadataObject.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetadataObject.cs new file mode 100644 index 0000000..f638cfd --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetadataObject.cs @@ -0,0 +1,29 @@ +using System; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models +{ + public abstract class MetadataObject : IComparable + { + ///Внутренний идентификатор объекта метаданных + public Guid Uuid { get; set; } = Guid.Empty; + ///Идентификатор файла объекта метаданных в таблице Config и DBNames + public Guid FileName { get; set; } = Guid.Empty; + ///Имя объекта метаданных + public string Name { get; set; } = string.Empty; + ///Синоним объекта метаданных + public string Alias { get; set; } = string.Empty; + + // TODO: add Comment property ? + + public int CompareTo(object other) + { + return CompareTo((MetadataObject)other); + } + public int CompareTo(MetadataObject other) + { + if (other == null) return 1; // this instance is bigger than other + return String.Compare(Name, other.Name, StringComparison.Ordinal); + } + public override string ToString() { return $"{GetType().Name}.{Name}"; } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetadataProperty.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetadataProperty.cs new file mode 100644 index 0000000..d5e6de7 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetadataProperty.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Linq; +using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Models +{ + /// Класс для описания свойств объекта метаданных + /// (реквизитов, измерений и ресурсов) + /// + public class MetadataProperty : MetadataObject + { + ///Основа имени поля в таблице СУБД (может быть дополнено постфиксами в зависимости от типа данных свойства) + public string DbName { get; set; } = string.Empty; + ///Коллекция для описания полей таблицы СУБД свойства объекта метаданных + public List Fields { get; set; } = new List(); + ///Логический смысл свойства. Подробнее смотри перечисление . + public PropertyPurpose Purpose { get; set; } = PropertyPurpose.Property; + ///Описание типов данных , которые могут использоваться для значений свойства. + public DataTypeInfo PropertyType { get; set; } = new DataTypeInfo(); + /// Вариант использования реквизита для групп и элементов + public PropertyUsage PropertyUsage { get; set; } = PropertyUsage.Item; + public bool IsPrimaryKey() + { + return (Fields != null + && Fields.Count > 0 + && Fields.Where(f => f.IsPrimaryKey).FirstOrDefault() != null); + } + public override string ToString() { return Name; } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetadataTokens.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetadataTokens.cs new file mode 100644 index 0000000..b6721bf --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/MetadataTokens.cs @@ -0,0 +1,122 @@ +// ReSharper disable InconsistentNaming +namespace YPermitin.SQLCLR.YellowMetadataReader.Models +{ + public static class MetadataTokens + { + #region "Постфиксы наименования полей таблиц СУБД и идентификаторы типов данных" + + ///Boolean (SQL table fields postfix) + public const string L = "L"; + ///Boolean (config file metadata) + public const string B = "B"; + ///Numeric + public const string N = "N"; + ///String + public const string S = "S"; + ///DateTime (config file metadata) + public const string D = "D"; + ///DateTime (SQL table fields postfix) + public const string T = "T"; + ///Reference type, УникальныйИдентификатор или ХранилищеЗначения + public const string R = "#"; + + public const string RRef = "RRef"; + public const string TRef = "TRef"; + public const string RRRef = "RRRef"; + public const string RTRef = "RTRef"; + public const string TYPE = "TYPE"; // 0x08 - reference data type + + #endregion + + #region "Токены объектов метаданных" + + ///Табличная часть (вложенный значимый тип данных) + public const string VT = "VT"; + ///Перечисление (ссылочный тип данных) + public const string Enum = "Enum"; + ///План видов характеристик (ссылочный тип данных) + public const string Chrc = "Chrc"; + ///Константа (значимый тип данных) + public const string Const = "Const"; + ///Регистр сведений (значимый тип данных) + public const string InfoRg = "InfoRg"; + ///План счетов (ссылочный тип данных) + public const string Acc = "Acc"; + ///Регистр бухгалтерии (значимый тип данных) + public const string AccRg = "AccRg"; + ///Операции регистра бухгалтерии, журнал проводок (зависимый значимый тип данных) + public const string AccRgED = "AccRgED"; + ///Регистр накопления (значимый тип данных) + public const string AccumRg = "AccumRg"; + ///Таблица итогов регистра накопления (зависимый значимый тип данных) + public const string AccumRgT = "AccumRgT"; + ///Таблица настроек регистра накопления (зависимый значимый тип данных) + public const string AccumRgOpt = "AccumRgOpt"; + ///Таблица изменений регистра накопления (зависимый значимый тип данных) + public const string AccumRgChngR = "AccumRgChngR"; + ///Документ (ссылочный тип данных) + public const string Document = "Document"; + ///Справочник (ссылочный тип данных) + public const string Reference = "Reference"; + ///План обмена (ссылочный тип данных) + public const string Node = "Node"; + ///Таблица изменений планов обмена (одна на каждый объект метаданных) + public const string ChngR = "ChngR"; + ///Хранилище метаданных конфигурации 1С + public const string Config = "Config"; + + #endregion + + #region "Токены полей таблиц СУБД объектов метаданных" + + public const string Fld = "Fld"; + public const string IDRRef = "IDRRef"; + public const string Version = "Version"; + public const string Marked = "Marked"; + public const string DateTime = "Date_Time"; + public const string NumberPrefix = "NumberPrefix"; + public const string Number = "Number"; + public const string Posted = "Posted"; + public const string PredefinedID = "PredefinedID"; + public const string Description = "Description"; + public const string Code = "Code"; + public const string OwnerID = "OwnerID"; + public const string Folder = "Folder"; + public const string ParentIDRRef = "ParentIDRRef"; + + public const string KeyField = "KeyField"; + public const string LineNo = "LineNo"; + public const string EnumOrder = "EnumOrder"; + public const string Type = "Type"; // ТипЗначения (ПланВидовХарактеристик) + + public const string Kind = "Kind"; // Тип счёта плана счетов (активный, пассивный, активно-пассивный) + public const string OrderField = "OrderField"; // Порядок счёта в плане счетов + public const string OffBalance = "OffBalance"; // Признак забалансового счёта плана счетов + public const string AccountDtRRef = "AccountDtRRef"; // Cчёт по дебету проводки регистра бухгалтерского учёта + public const string AccountCtRRef = "AccountCtRRef"; // Cчёт по кредиту проводки регистра бухгалтерского учёта + public const string EDHashDt = "EDHashDt"; // Хэш проводки по дебету регистра бухгалтерского учёта + public const string EDHashCt = "EDHashCt"; // Хэш проводки по кредиту регистра бухгалтерского учёта + public const string Period = "Period"; + public const string Periodicity = "Periodicity"; + public const string ActualPeriod = "ActualPeriod"; + public const string Recorder = "Recorder"; + public const string RecorderRRef = "RecorderRRef"; + public const string RecorderTRef = "RecorderTRef"; + public const string Active = "Active"; + public const string RecordKind = "RecordKind"; + public const string SentNo = "SentNo"; + public const string ReceivedNo = "ReceivedNo"; + + public const string Splitter = "Splitter"; + public const string NodeTRef = "NodeTRef"; + public const string NodeRRef = "NodeRRef"; + public const string MessageNo = "MessageNo"; + public const string UseTotals = "UseTotals"; + public const string UseSplitter = "UseSplitter"; + public const string MinPeriod = "MinPeriod"; + public const string MinCalculatedPeriod = "MinCalculatedPeriod"; + public const string RepetitionFactor = "RepetitionFactor"; + + #endregion + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/SqlFieldInfo.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/SqlFieldInfo.cs new file mode 100644 index 0000000..37a9daa --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Models/SqlFieldInfo.cs @@ -0,0 +1,21 @@ +// ReSharper disable InconsistentNaming +namespace YPermitin.SQLCLR.YellowMetadataReader.Models +{ + public class SqlFieldInfo + { + // ReSharper disable once EmptyConstructor + public SqlFieldInfo() { } + public int ORDINAL_POSITION; + public string COLUMN_NAME; + public string DATA_TYPE; + public int CHARACTER_OCTET_LENGTH; + public int CHARACTER_MAXIMUM_LENGTH; + public byte NUMERIC_PRECISION; + public byte NUMERIC_SCALE; + public bool IS_NULLABLE; + public override string ToString() + { + return COLUMN_NAME + " (" + DATA_TYPE + ")"; + } + } +} diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Properties/AssemblyInfo.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b712636 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// Общие сведения об этой сборке предоставляются следующим набором +// набора атрибутов. Измените значения этих атрибутов для изменения сведений, +// связанные со сборкой. +[assembly: AssemblyTitle("YellowMetadataReader")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("YellowMetadataReader")] +[assembly: AssemblyCopyright("Copyright © 2023")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Установка значения False для параметра ComVisible делает типы в этой сборке невидимыми +// для компонентов COM. Если необходимо обратиться к типу в этой сборке через +// COM, задайте атрибуту ComVisible значение TRUE для этого типа. +[assembly: ComVisible(false)] + +// Следующий GUID служит для идентификации библиотеки типов, если этот проект будет видимым для COM +[assembly: Guid("c4503533-bfbd-4e7e-a00e-941746748ddd")] + +// Сведения о версии сборки состоят из указанных ниже четырех значений: +// +// Основной номер версии +// Дополнительный номер версии +// Номер сборки +// Редакция +// +// Можно задать все значения или принять номера сборки и редакции по умолчанию +// используя "*", как показано ниже: +// [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/Services/ConfigFileParser.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/ConfigFileParser.cs new file mode 100644 index 0000000..28d855f --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/ConfigFileParser.cs @@ -0,0 +1,95 @@ +using System.IO; +using YPermitin.SQLCLR.YellowMetadataReader.Models; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Services +{ + public sealed class ConfigFileParser + { + public ConfigObject Parse(StreamReader stream) + { + return ParseFile(stream, null); + } + private ConfigObject ParseFile(StreamReader stream, ConfigObject parent) + { + char c; + string value = null; + ConfigObject cfo = null; + while (!stream.EndOfStream) + { + do + { + c = (char)stream.Read(); + } + while (c == '\r' || c == '\n'); + + if (c == '{') // start of object + { + cfo = new ConfigObject(); + ParseFile(stream, cfo); + if (parent != null) // this is child object + { + parent.Values.Add(cfo); + } + } + else if (c == '}') // end of object + { + if (value != null) + { + parent?.Values.Add(value); + } + return cfo; + } + else if (c == '"') // start of string value + { + value = string.Empty; + while (!stream.EndOfStream) + { + c = (char)stream.Read(); + if (c == '"') // might be end of string + { + c = (char)stream.Read(); + if (c == '"') // double quotes - this is not the end + { + value += c; + } + else // this is the end + { + parent?.Values.Add(value); + value = null; + if (c == '}') // end of object + { + return cfo; + } + break; + } + } + else + { + value += c; + } + } + } + else if (c == ',') // end of value + { + if (value != null) + { + parent?.Values.Add(value); + value = null; + } + } + else // number or uuid value + { + if (value == null) + { + value = c.ToString(); + } + else + { + value += c; + } + } + } + return cfo; + } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/ConfigFileReader.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/ConfigFileReader.cs new file mode 100644 index 0000000..487afd4 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/ConfigFileReader.cs @@ -0,0 +1,280 @@ +using System; +using System.Data; +using System.Data.Common; +using System.Data.SqlClient; +using System.IO; +using System.IO.Compression; +using System.Text; +using YPermitin.SQLCLR.YellowMetadataReader.Models; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Services +{ + /// + /// Интерфейс для чтения файлов конфигурации 1С из таблиц IBVersion, DBSchema, Params и Config + /// + public interface IConfigFileReader + { + ///Возвращает установленную ранее строку подключения к базе данных 1С + string ConnectionString { get; } + string DatabaseName { get; } + + ///Устанавливает строку подключения к базе данных 1С + ///Строка подключения к базе данных 1С + void UseConnectionString(string connectionString); + void UseDatabaseName(string databaseName); + + ///Формирует строку подключения к базе данных 1С по параметрам + ///Имя или сетевой адрес сервера SQL Server + ///Имя базы данных SQL Server + ///Имя пользователя (если не указано, используется Windows аутентификация) + ///Пароль пользователя SQL Server (используется только в случае SQL Server аутентификации) + void ConfigureConnectionString(string server, string database, string userName, string password); + + ///Получает требуемую версию платформы 1С для работы с базой данных + ///Требуемая версия платформы 1С + int GetPlatformRequiredVersion(); + + ///Получает количество лет, используемое платформой 1С, для добавления к значениям дат + ///Количество лет, добавляемое к датам. Значения по умолчанию: SQL Server = 2000, PostrgeSQL = 0. + int GetYearOffset(); + + ///Получает файл метаданных в "сыром" (как есть) бинарном виде + ///Имя файла метаданных: root, DBNames или значение UUID + ///Бинарные данные файла метаданных + byte[] ReadBytes(string fileName); + byte[] ReadParamsFile(string fileName); + byte[] ReadConfigFile(string fileName); + + ///Функция определяет является ли форматом файла метаданных UTF-8 + ///Бинарные данные файла метаданных + ///true - формат файла UTF-8; false - формат файла другой (deflate) + bool IsUtf8(byte[] fileData); + + StreamReader CreateReader(byte[] fileData); + StreamReader CreateStreamReader(byte[] fileData); + ///Распаковывает файл метаданных по алгоритму deflate и создаёт поток для чтения в формате UTF-8 + ///Бинарные данные файла метаданных + ///Поток для чтения файла метаданных в формате UTF-8 + StreamReader CreateDeflateReader(byte[] fileData); + + ///Читает файл конфигурации и формирует его данные в виде древовидной структуры + /// Имя файла метаданных: root, DBNames, DBSchema или UUID файла + /// Дерево значений файла конфигурации + ConfigObject ReadConfigObject(string fileName); + + string ReadConfigObjectAsString(string fileName); + } + + /// + /// Класс для чтения файлов конфигурации 1С из SQL Server + /// + public sealed class ConfigFileReader : IConfigFileReader + { + #region "Constants" + + private const string RootFileName = "root"; // Config + private const string DbnamesFileName = "DBNames"; // Params + private const string DbschemaFileName = "DBSchema"; // DBSchema + + private const string MsIbversionQueryScript = "SELECT TOP 1 [PlatformVersionReq] FROM [{DatabaseName}].[dbo].[IBVersion];"; + private const string MsParamsQueryScript = "SELECT [BinaryData] FROM [{DatabaseName}].[dbo].[Params] WHERE [FileName] = @FileName;"; + private const string MsConfigQueryScript = "SELECT [BinaryData] FROM [{DatabaseName}].[dbo].[Config] WHERE [FileName] = @FileName;"; // Version 8.3 ORDER BY [PartNo] ASC"; + private const string MsDbschemaQueryScript = "SELECT TOP 1 [SerializedData] FROM [{DatabaseName}].[dbo].[DBSchema];"; + private const string MsYearoffsetQueryScript = "SELECT TOP 1 [Offset] FROM [{DatabaseName}].[dbo].[_YearOffset];"; + + #endregion + + private readonly ConfigFileParser _fileParser = new ConfigFileParser(); + + public string ConnectionString { get; private set; } = string.Empty; + public string DatabaseName { get; private set; } = string.Empty; + private byte[] CombineArrays(byte[] a1, byte[] a2) + { + if (a1 == null) return a2; + + byte[] result = new byte[a1.Length + a2.Length]; + Buffer.BlockCopy(a1, 0, result, 0, a1.Length); + Buffer.BlockCopy(a2, 0, result, a1.Length, a2.Length); + return result; + } + private DbConnection CreateDbConnection() + { + return new SqlConnection(ConnectionString); + } + private void ConfigureFileNameParameter(DbCommand command, string fileName) + { + if (string.IsNullOrWhiteSpace(fileName)) return; + + ((SqlCommand)command).Parameters.AddWithValue("FileName", fileName); + } + private T ExecuteScalar(string script, string fileName) + { + T result = default(T); + using (DbConnection connection = CreateDbConnection()) + using (DbCommand command = connection.CreateCommand()) + { + command.CommandText = script.Replace("{DatabaseName}", DatabaseName); + command.CommandType = CommandType.Text; + ConfigureFileNameParameter(command, fileName); + connection.Open(); + object value = command.ExecuteScalar(); + if (value != null) + { + result = (T)value; + } + } + return result; + } + private byte[] ExecuteReader(string script, string fileName) + { + byte[] fileData = null; + using (DbConnection connection = CreateDbConnection()) + using (DbCommand command = connection.CreateCommand()) + { + command.CommandText = script.Replace("{DatabaseName}", DatabaseName); + command.CommandType = CommandType.Text; + ConfigureFileNameParameter(command, fileName); + connection.Open(); + using (DbDataReader reader = command.ExecuteReader()) + { + while (reader.Read()) + { + byte[] data = (byte[])reader[0]; + fileData = CombineArrays(fileData, data); + } + } + } + return fileData; + } + public void UseConnectionString(string connectionString) + { + ConnectionString = connectionString; + } + public void UseDatabaseName(string databaseName) + { + DatabaseName = databaseName; + } + public void ConfigureConnectionString(string server, string database, string userName, string password) + { + ConfigureConnectionStringForSQLServer(server, database, userName, password); + } + private void ConfigureConnectionStringForSQLServer(string server, string database, string userName, string password) + { + SqlConnectionStringBuilder connectionString = new SqlConnectionStringBuilder() + { + DataSource = server, + InitialCatalog = database + }; + if (!string.IsNullOrWhiteSpace(userName)) + { + connectionString.UserID = userName; + connectionString.Password = password; + } + connectionString.IntegratedSecurity = string.IsNullOrWhiteSpace(userName); + ConnectionString = connectionString.ToString(); + } + + public int GetPlatformRequiredVersion() + { + return ExecuteScalar(MsIbversionQueryScript, null); + } + public int GetYearOffset() + { + return ExecuteScalar(MsYearoffsetQueryScript, null); + } + + public bool IsUtf8(byte[] fileData) + { + if (fileData == null) throw new ArgumentNullException(nameof(fileData)); + + if (fileData.Length < 3) return false; + + return fileData[0] == 0xEF // (b)yte + && fileData[1] == 0xBB // (o)rder + && fileData[2] == 0xBF; // (m)ark + } + public byte[] ReadBytes(string fileName) + { + switch (fileName) + { + case RootFileName: + return ExecuteReader(MsConfigQueryScript, fileName); + case DbnamesFileName: + return ExecuteReader(MsParamsQueryScript, fileName); + case DbschemaFileName: + return ExecuteReader(MsDbschemaQueryScript, fileName); + default: + return ExecuteReader(MsConfigQueryScript, fileName); + } + } + public byte[] ReadParamsFile(string fileName) + { + return ExecuteReader(MsParamsQueryScript, fileName); + } + public byte[] ReadConfigFile(string fileName) + { + return ExecuteReader(MsConfigQueryScript, fileName); + } + public StreamReader CreateReader(byte[] fileData) + { + if (IsUtf8(fileData)) + { + return CreateStreamReader(fileData); + } + return CreateDeflateReader(fileData); + } + public StreamReader CreateStreamReader(byte[] fileData) + { + MemoryStream memory = new MemoryStream(fileData); + return new StreamReader(memory, Encoding.UTF8); + } + public StreamReader CreateDeflateReader(byte[] fileData) + { + MemoryStream memory = new MemoryStream(fileData); + DeflateStream stream = new DeflateStream(memory, CompressionMode.Decompress); + + return new StreamReader(stream, Encoding.UTF8); + } + public ConfigObject ReadConfigObject(string fileName) + { + byte[] fileData = ReadBytes(fileName); + + if (fileData == null) + { + return null; // file name is not found + } + + using (StreamReader stream = CreateReader(fileData)) + { + return _fileParser.Parse(stream); + } + } + + public string ReadConfigObjectAsString(string fileName) + { + byte[] fileData = ReadBytes(fileName); + + if (fileData == null) + { + return null; // file name is not found + } + + StringBuilder dataAsString = new StringBuilder(); + + using (StreamReader reader = CreateReader(fileData)) + { + string stringLine; + do + { + stringLine = reader.ReadLine(); + if (stringLine != null) + { + dataAsString.AppendLine(stringLine); + } + } while (stringLine != null); + } + + return dataAsString.ToString(); + } + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/Configurator.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/Configurator.cs new file mode 100644 index 0000000..9da89f7 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/Configurator.cs @@ -0,0 +1,1535 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using YPermitin.SQLCLR.YellowMetadataReader.Enrichers; +using YPermitin.SQLCLR.YellowMetadataReader.Enrichers.Converters; +using YPermitin.SQLCLR.YellowMetadataReader.Models; +using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums; +using YPermitin.SQLCLR.YellowMetadataReader.Models.Interfaces; +using YPermitin.SQLCLR.YellowMetadataReader.Models.MetaObjects; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Services +{ + public sealed class Configurator + { + internal InfoBase InfoBase; + internal readonly IConfigFileReader FileReader; + internal readonly ISqlMetadataReader SqlMetadataReader; + private readonly Dictionary _enrichers = new Dictionary(); + private readonly Dictionary _converters = new Dictionary(); + + private readonly Dictionary _metadataTypes = new Dictionary() + { + { MetadataTokens.Acc, typeof(Account) }, + { MetadataTokens.AccRg, typeof(AccountingRegister) }, + { MetadataTokens.AccumRg, typeof(AccumulationRegister) }, + { MetadataTokens.Reference, typeof(Catalog) }, + { MetadataTokens.Chrc, typeof(Characteristic) }, + { MetadataTokens.Const, typeof(Constant) }, + { MetadataTokens.Document, typeof(Document) }, + { MetadataTokens.Enum, typeof(Enumeration) }, + { MetadataTokens.InfoRg, typeof(InformationRegister) }, + { MetadataTokens.Node, typeof(Publication) }, + { MetadataTokens.VT, typeof(TablePart) }, + { MetadataTokens.AccumRgT, typeof(AccumulationRegisterTotal) } + }; + private readonly Dictionary> _factories = new Dictionary>() + { + { typeof(Account), () => { return new Account(); } }, + { typeof(AccountingRegister), () => { return new AccountingRegister(); } }, + { typeof(AccumulationRegister), () => { return new AccumulationRegister(); } }, + { typeof(Catalog), () => { return new Catalog(); } }, + { typeof(Characteristic), () => { return new Characteristic(); } }, + { typeof(Constant), () => { return new Constant(); } }, + { typeof(Document), () => { return new Document(); } }, + { typeof(Enumeration), () => { return new Enumeration(); } }, + { typeof(InformationRegister), () => { return new InformationRegister(); } }, + { typeof(Publication), () => { return new Publication(); } }, + { typeof(TablePart), () => { return new TablePart(); } }, + { typeof(AccumulationRegisterTotal), () => { return new AccumulationRegisterTotal(); } }, + { typeof(ApplicationObject), () => { return new ApplicationObject(); } } + }; + + public Configurator(IConfigFileReader fileReader, ISqlMetadataReader sqlMetadataReader) + { + FileReader = fileReader ?? throw new ArgumentNullException(); + SqlMetadataReader = sqlMetadataReader ?? throw new ArgumentNullException(); + + InfoBase = new InfoBase(); + InitializeConverters(); + InitializeEnrichers(); + } + + private void InitializeConverters() + { + _converters.Add(typeof(DataTypeInfo), new DataTypeInfoConverter(this)); + } + private void InitializeEnrichers() + { + _enrichers.Add(typeof(DbNamesEnricher), new DbNamesEnricher(this)); + _enrichers.Add(typeof(InfoBase), new InfoBaseEnricher(this)); + _enrichers.Add(typeof(Enumeration), new EnumerationEnricher(this)); + _enrichers.Add(typeof(Catalog), new CatalogEnricher(this)); + _enrichers.Add(typeof(Characteristic), new CharacteristicEnricher(this)); + _enrichers.Add(typeof(Document), new DocumentEnricher(this)); + _enrichers.Add(typeof(Publication), new PublicationEnricher(this)); + _enrichers.Add(typeof(InformationRegister), new InformationRegisterEnricher(this)); + _enrichers.Add(typeof(AccumulationRegister), new AccumulationRegisterEnricher(this)); + _enrichers.Add(typeof(AccumulationRegisterTotal), new AccumulationRegisterTotalEnricher(this)); + _enrichers.Add(typeof(Constant), new ConstantEnricher(this)); + _enrichers.Add(typeof(Account), new AccountEnricher(this)); + _enrichers.Add(typeof(AccountingRegister), new AccountingRegisterEnricher(this)); + } + + public InfoBase OpenInfoBase(OpenInfobaseLevel level = OpenInfobaseLevel.ConfigFull) + { + try + { + GetEnricher(typeof(DbNamesEnricher)).Enrich(InfoBase); + } + catch (Exception error) + { + throw new Exception("Failed to load [DBNames] params file.", error); + } + + try + { + GetEnricher().Enrich(InfoBase); + } + catch (Exception error) + { + throw new Exception("Failed to load [root] config file.", error); + } + + if (level == OpenInfobaseLevel.ConfigFull) + { + OpenInfoBaseSynchronously(); + } + + return InfoBase; + } + private bool TryEnrichMetadataObject(IContentEnricher enricher, MetadataObject metadataObject) + { + if (enricher == null) throw new ArgumentNullException(nameof(TryEnrichMetadataObject) + "(" + nameof(enricher) + ")"); + if (metadataObject == null) throw new ArgumentNullException(nameof(TryEnrichMetadataObject) + "(" + nameof(metadataObject) + ")"); + + try + { + enricher.Enrich(metadataObject); + return true; + } + catch + { + return false; + } + } + private void OpenInfoBaseSynchronously() + { + IContentEnricher enricher = GetEnricher(); + foreach (var o in InfoBase.Enumerations.Values) + { + var enumeration = (Enumeration)o; + if (TryEnrichMetadataObject(enricher, enumeration)) + { + _ = InfoBase.ReferenceTypeUuids.TryAdd(enumeration.FullKey, enumeration); + _ = InfoBase.ReferenceTypeCodes.TryAdd(enumeration.TypeCode, enumeration); + } + } + + enricher = GetEnricher(); + foreach (var o in InfoBase.Characteristics.Values) + { + var characteristic = (Characteristic)o; + if (TryEnrichMetadataObject(enricher, characteristic)) + { + InfoBase.CharacteristicTypes.Add(characteristic.TypeUuid, characteristic); + _ = InfoBase.ReferenceTypeUuids.TryAdd(characteristic.FullKey, characteristic); + _ = InfoBase.ReferenceTypeCodes.TryAdd(characteristic.TypeCode, characteristic); + } + } + + enricher = GetEnricher(); + foreach (var o in InfoBase.Catalogs.Values) + { + var catalog = (Catalog)o; + if (TryEnrichMetadataObject(enricher, catalog)) + { + _ = InfoBase.ReferenceTypeUuids.TryAdd(catalog.FullKey, catalog); + _ = InfoBase.ReferenceTypeCodes.TryAdd(catalog.TypeCode, catalog); + } + } + + enricher = GetEnricher(); + foreach (var o in InfoBase.Documents.Values) + { + var document = (Document)o; + if (TryEnrichMetadataObject(enricher, document)) + { + _ = InfoBase.ReferenceTypeUuids.TryAdd(document.FullKey, document); + _ = InfoBase.ReferenceTypeCodes.TryAdd(document.TypeCode, document); + } + } + + enricher = GetEnricher(); + foreach (var o in InfoBase.Publications.Values) + { + var publication = (Publication)o; + if (TryEnrichMetadataObject(enricher, publication)) + { + _ = InfoBase.ReferenceTypeUuids.TryAdd(publication.FullKey, publication); + _ = InfoBase.ReferenceTypeCodes.TryAdd(publication.TypeCode, publication); + } + } + + enricher = GetEnricher(); + foreach (var o in InfoBase.InformationRegisters.Values) + { + var register = (InformationRegister)o; + _ = TryEnrichMetadataObject(enricher, register); + } + + enricher = GetEnricher(); + foreach (var o in InfoBase.AccumulationRegisters.Values) + { + var register = (AccumulationRegister)o; + _ = TryEnrichMetadataObject(enricher, register); + } + + enricher = GetEnricher(); + foreach (var o in InfoBase.Constants.Values) + { + var register = (Constant)o; + _ = TryEnrichMetadataObject(enricher, register); + } + + enricher = GetEnricher(); + foreach (var o in InfoBase.Accounts.Values) + { + var register = (Account)o; + _ = TryEnrichMetadataObject(enricher, register); + } + + enricher = GetEnricher(); + foreach (var o in InfoBase.AccountingRegisters.Values) + { + var register = (AccountingRegister)o; + _ = TryEnrichMetadataObject(enricher, register); + } + + enricher = GetEnricher(); + var accumulationRegisterTotals = InfoBase + .AccumulationRegisters.Values + .SelectMany(e => e.NestedObjects) + .Where(e => e is AccumulationRegisterTotal) + .ToList(); + foreach (var o in accumulationRegisterTotals) + { + var register = (AccumulationRegisterTotal)o; + _ = TryEnrichMetadataObject(enricher, register); + } + } + + #region "DbNames" + + public string CreateDbName(string token, int code) + { + if (code <= 0) + { + return $"_{token}"; + } + else + { + return $"_{token}{code}"; + } + } + public Type GetTypeByToken(string token) + { + if (string.IsNullOrWhiteSpace(token)) return null; + + if (_metadataTypes.TryGetValue(token, out Type type)) + { + return type; + } + return typeof(ApplicationObject); + } + public Func GetFactory(string token) + { + Type type = GetTypeByToken(token); + if (type == null) return null; + + if (_factories.TryGetValue(type, out Func factory)) + { + return factory; + } + + return null; + } + public ApplicationObject CreateObject(Guid uuid, string token, int code) + { + Func factory = GetFactory(token); + if (factory == null) return null; + + ApplicationObject metaObject = factory(); + metaObject.FileName = uuid; + metaObject.TypeCode = code; + metaObject.Token = token; + metaObject.TableName = CreateDbName(token, code); + + return metaObject; + } + public MetadataProperty CreateProperty(Guid uuid, string token, int code) + { + return new MetadataProperty() + { + Uuid = uuid, + FileName = uuid, + DbName = CreateDbName(token, code) + }; + } + + #endregion + + public IContentEnricher GetEnricher() where T : MetadataObject + { + return GetEnricher(typeof(T)); + } + public IContentEnricher GetEnricher(Type type) + { + if (_enrichers.TryGetValue(type, out IContentEnricher enricher)) + { + return enricher; + } + return null; + } + public IConfigObjectConverter GetConverter() + { + return GetConverter(typeof(T)); + } + public IConfigObjectConverter GetConverter(Type type) + { + if (_converters.TryGetValue(type, out IConfigObjectConverter converter)) + { + return converter; + } + return null; + } + + public void ConfigureProperties(ApplicationObject metaObject, ConfigObject properties, PropertyPurpose purpose) + { + int propertiesCount = properties.GetInt32(new[] { 1 }); // количество реквизитов + if (propertiesCount == 0) return; + + int propertyOffset = 2; + for (int p = 0; p < propertiesCount; p++) + { + // P.0.1.1.1.1.2 - property uuid + Guid propertyUuid = properties.GetUuid(new[] { p + propertyOffset, 0, 1, 1, 1, 1, 2 }); + // P.0.1.1.1.2 - property name + string propertyName = properties.GetString(new[] { p + propertyOffset, 0, 1, 1, 1, 2 }); + // P.0.1.1.1.3 - property alias descriptor + string propertyAlias = string.Empty; + ConfigObject aliasDescriptor = properties.GetObject(new[] { p + propertyOffset, 0, 1, 1, 1, 3 }); + if (aliasDescriptor.Values.Count == 3) + { + // P.0.1.1.1.3.2 - property alias + propertyAlias = properties.GetString(new[] { p + propertyOffset, 0, 1, 1, 1, 3, 2 }); + } + // P.0.1.1.2 - property types + ConfigObject propertyTypes = properties.GetObject(new[] { p + propertyOffset, 0, 1, 1, 2 }); + // P.0.1.1.2.0 = "Pattern" + DataTypeInfo typeInfo = (DataTypeInfo)GetConverter().Convert(propertyTypes); + + // P.0.3 - property usage for catalogs and characteristics + int propertyUsage = -1; + if (metaObject is Catalog || metaObject is Characteristic) + { + propertyUsage = properties.GetInt32(new[] { p + propertyOffset, 0, 3 }); + } + + ConfigureProperty(metaObject, purpose, propertyUuid, propertyName, propertyAlias, typeInfo, propertyUsage); + } + } + public void ConfigureProperty(ApplicationObject metaObject, PropertyPurpose purpose, Guid fileName, string name, string alias, DataTypeInfo type, int propertyUsage) + { + if (!InfoBase.PropertiesById.TryGetValue(fileName, out MetadataProperty property)) return; + + // TODO Проверяем существует ли это поле в базе данных + // Если нет, то проверяем некоторые другие правила формирования полей. + // Если правила не подходят, то добавляем поле как есть в любом случае. + // metaObject.TableName + // property.DbName + if (metaObject is AccountingRegister) + { + var dbTableFields = SqlMetadataReader.GetSqlFieldsOrderedByName(metaObject.TableName); + if (!dbTableFields.Any(e => e.COLUMN_NAME == property.DbName)) + { + // Поля БД не найдено. Пытаемся определить есть ли соответствующие поля ДТ / КТ + string newDatabaseFieldNameDt = $"{property.DbName}DtRRef"; + bool fieldDbDtExists = dbTableFields.Any(e => e.COLUMN_NAME == newDatabaseFieldNameDt); + if (!fieldDbDtExists) + { + newDatabaseFieldNameDt = $"{property.DbName}Dt"; + fieldDbDtExists = dbTableFields.Any(e => e.COLUMN_NAME == newDatabaseFieldNameDt); + } + string newDatabaseFieldNameCt = $"{property.DbName}CtRRef"; + bool fieldDbCtExists = dbTableFields.Any(e => e.COLUMN_NAME == newDatabaseFieldNameCt); + if (!fieldDbCtExists) + { + newDatabaseFieldNameCt = $"{property.DbName}Ct"; + fieldDbCtExists = dbTableFields.Any(e => e.COLUMN_NAME == newDatabaseFieldNameCt); + } + + if (fieldDbDtExists && fieldDbCtExists) + { + MetadataProperty newPropertyDt = new MetadataProperty(); + newPropertyDt.DbName = newDatabaseFieldNameDt; + newPropertyDt.Name = $"{name}Дт"; + newPropertyDt.Alias = $"{alias} Дт"; + newPropertyDt.Purpose = purpose; + newPropertyDt.PropertyType = type; + newPropertyDt.Fields = property.Fields; + newPropertyDt.PropertyUsage = property.PropertyUsage; + newPropertyDt.Uuid = property.Uuid; + newPropertyDt.FileName = property.FileName; + if (propertyUsage != -1) + { + newPropertyDt.PropertyUsage = (PropertyUsage)propertyUsage; + } + + metaObject.Properties.Add(newPropertyDt); + ConfigureDatabaseFields(newPropertyDt); + + MetadataProperty newPropertyCt = new MetadataProperty(); + newPropertyCt.DbName = newDatabaseFieldNameCt; + newPropertyCt.Name = $"{name}Кт"; + newPropertyCt.Alias = $"{alias} Кт"; + newPropertyCt.Purpose = purpose; + newPropertyCt.PropertyType = type; + newPropertyCt.Fields = property.Fields; + newPropertyCt.PropertyUsage = property.PropertyUsage; + newPropertyCt.Uuid = property.Uuid; + newPropertyCt.FileName = property.FileName; + if (propertyUsage != -1) + { + newPropertyCt.PropertyUsage = (PropertyUsage)propertyUsage; + } + + metaObject.Properties.Add(newPropertyCt); + ConfigureDatabaseFields(newPropertyCt); + + return; + } + } + } + + property.Name = name; + property.Alias = alias; + property.Purpose = purpose; + property.PropertyType = type; + if (propertyUsage != -1) + { + property.PropertyUsage = (PropertyUsage)propertyUsage; + } + metaObject.Properties.Add(property); + + ConfigureDatabaseFields(property); + } + + public void ConfigureSharedProperties(ApplicationObject metaObject) + { + foreach (SharedProperty property in InfoBase.SharedProperties.Values) + { + if (property.UsageSettings.TryGetValue(metaObject.FileName, out SharedPropertyUsage usage)) + { + if (usage == SharedPropertyUsage.Use) + { + metaObject.Properties.Add(property); + } + } + else // Auto + { + if (property.AutomaticUsage == AutomaticUsage.Use) + { + metaObject.Properties.Add(property); + } + } + } + } + + #region "TableParts" + + public void ConfigureTableParts(ApplicationObject owner, ConfigObject tableParts) + { + int tablePartsCount = tableParts.GetInt32(new[] { 1 }); // количество табличных частей + if (tablePartsCount == 0) return; + + int offset = 2; + for (int t = 0; t < tablePartsCount; t++) + { + // T.0.1.5.1.1.2 - uuid табличной части + Guid uuid = tableParts.GetUuid(new[] { t + offset, 0, 1, 5, 1, 1, 2 }); + // T.0.1.5.1.2 - имя табличной части + string name = tableParts.GetString(new[] { t + offset, 0, 1, 5, 1, 2 }); + if (InfoBase.TablePartsById.TryGetValue(uuid, out ApplicationObject tablePart)) + { + if (tablePart is TablePart) + { + tablePart.Name = name; + ((TablePart)tablePart).Owner = owner; + tablePart.TableName = owner.TableName + tablePart.TableName; + owner.TableParts.Add((TablePart)tablePart); + + // T.2 - коллекция реквизитов табличной части (ConfigObject) + // T.2.0 = 888744e1-b616-11d4-9436-004095e12fc7 - идентификатор коллекции реквизитов табличной части + // T.2.1 - количество реквизитов табличной части + Guid collectionUuid = tableParts.GetUuid(new[] { t + offset, 2, 0 }); + if (collectionUuid == new Guid("888744e1-b616-11d4-9436-004095e12fc7")) + { + ConfigurePropertyСсылка(owner, tablePart); + ConfigurePropertyКлючСтроки(tablePart); + ConfigurePropertyНомерСтроки(tablePart); + + ConfigObject properties = tableParts.GetObject(new[] { t + offset, 2 }); + ConfigureProperties(tablePart, properties, PropertyPurpose.Property); + } + } + } + } + } + private void ConfigurePropertyСсылка(ApplicationObject owner, ApplicationObject tablePart) + { + MetadataProperty property = new MetadataProperty() + { + Name = "Ссылка", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = owner.TableName + "_IDRRef" + }; + property.PropertyType.IsUuid = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 16, + TypeName = "binary", + KeyOrdinal = 1, + IsPrimaryKey = true + }); + tablePart.Properties.Add(property); + } + private void ConfigurePropertyКлючСтроки(ApplicationObject tablePart) + { + MetadataProperty property = new MetadataProperty() + { + Name = "КлючСтроки", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_KeyField" + }; + property.PropertyType.IsBinary = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 4, + TypeName = "binary", + KeyOrdinal = 2, + IsPrimaryKey = true + }); + tablePart.Properties.Add(property); + } + private void ConfigurePropertyНомерСтроки(ApplicationObject tablePart) + { + if (!InfoBase.Properties.TryGetValue(tablePart.FullKey, out MetadataProperty property)) return; + + property.Name = "НомерСтроки"; + property.PropertyType.CanBeNumeric = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 5, + Precision = 5, + TypeName = "numeric" + }); + + tablePart.Properties.Add(property); + } + + #endregion + + public void ConfigureDatabaseFields(MetadataProperty property) + { + if (property.PropertyType.IsMultipleType) + { + ConfigureDatabaseFieldsForMultipleType(property); + } + else + { + ConfigureDatabaseFieldsForSingleType(property); + } + } + private void ConfigureDatabaseFieldsForSingleType(MetadataProperty property) + { + if (property.PropertyType.IsUuid) + { + property.Fields.Add(new DatabaseField(property.DbName, "binary", 16)); + } + else if (property.PropertyType.IsBinary) + { + // is used only for system properties of system types + // TODO: log if it happens eventually + } + else if (property.PropertyType.IsValueStorage) + { + property.Fields.Add(new DatabaseField(property.DbName, "varbinary", -1)); + } + else if (property.PropertyType.CanBeString) + { + if (property.PropertyType.StringKind == StringKind.Fixed) + { + property.Fields.Add(new DatabaseField(property.DbName, "nchar", property.PropertyType.StringLength)); + } + else + { + property.Fields.Add(new DatabaseField(property.DbName, "nvarchar", property.PropertyType.StringLength)); + } + } + else if (property.PropertyType.CanBeNumeric) + { + // length can be updated from database + property.Fields.Add(new DatabaseField( + property.DbName, + "numeric", 9, + property.PropertyType.NumericPrecision, + property.PropertyType.NumericScale)); + } + else if (property.PropertyType.CanBeBoolean) + { + property.Fields.Add(new DatabaseField(property.DbName, "binary", 1)); + } + else if (property.PropertyType.CanBeDateTime) + { + // length, precision and scale can be updated from database + property.Fields.Add(new DatabaseField(property.DbName, "datetime2", 6, 19, 0)); + } + else if (property.PropertyType.CanBeReference) + { + property.Fields.Add(new DatabaseField(property.DbName + MetadataTokens.RRef, "binary", 16)); + } + } + private void ConfigureDatabaseFieldsForMultipleType(MetadataProperty property) + { + property.Fields.Add(new DatabaseField(property.DbName + "_" + MetadataTokens.TYPE, "binary", 1) + { + Purpose = FieldPurpose.Discriminator + }); + + if (property.PropertyType.CanBeString) + { + if (property.PropertyType.StringKind == StringKind.Fixed) + { + property.Fields.Add(new DatabaseField( + property.DbName + "_" + MetadataTokens.S, + "nchar", + property.PropertyType.StringLength) + { + Purpose = FieldPurpose.String + }); + } + else + { + property.Fields.Add(new DatabaseField( + property.DbName + "_" + MetadataTokens.S, + "nvarchar", + property.PropertyType.StringLength) + { + Purpose = FieldPurpose.String + }); + } + } + if (property.PropertyType.CanBeNumeric) + { + // length can be updated from database + property.Fields.Add(new DatabaseField( + property.DbName + "_" + MetadataTokens.N, + "numeric", 9, + property.PropertyType.NumericPrecision, + property.PropertyType.NumericScale) + { + Purpose = FieldPurpose.Numeric + }); + + } + if (property.PropertyType.CanBeBoolean) + { + property.Fields.Add(new DatabaseField(property.DbName + "_" + MetadataTokens.L, "binary", 1) + { + Purpose = FieldPurpose.Boolean + }); + } + if (property.PropertyType.CanBeDateTime) + { + // length, precision and scale can be updated from database + property.Fields.Add(new DatabaseField(property.DbName + "_" + MetadataTokens.T, "datetime2", 6, 19, 0) + { + Purpose = FieldPurpose.DateTime + }); + } + if (property.PropertyType.CanBeReference) + { + if (property.PropertyType.ReferenceTypeUuid == Guid.Empty) // miltiple refrence type + { + property.Fields.Add(new DatabaseField(property.DbName + "_" + MetadataTokens.RTRef, "binary", 4) + { + Purpose = FieldPurpose.TypeCode + }); + } + property.Fields.Add(new DatabaseField(property.DbName + "_" + MetadataTokens.RRRef, "binary", 16) + { + Purpose = FieldPurpose.Object + }); + } + } + + #region "Reference type system properties" + + public void ConfigurePropertyСсылка(ApplicationObject metaObject) + { + MetadataProperty property = new MetadataProperty() + { + Name = "Ссылка", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_IDRRef" + }; + property.PropertyType.IsUuid = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 16, + TypeName = "binary", + KeyOrdinal = 1, + IsPrimaryKey = true + }); + metaObject.Properties.Add(property); + } + public void ConfigurePropertyВерсияДанных(ApplicationObject metaObject) + { + MetadataProperty property = new MetadataProperty() + { + Name = "ВерсияДанных", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_Version" + }; + property.PropertyType.IsBinary = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 8, + TypeName = "timestamp" + }); + metaObject.Properties.Add(property); + } + public void ConfigurePropertyПометкаУдаления(ApplicationObject metaObject) + { + MetadataProperty property = new MetadataProperty() + { + Name = "ПометкаУдаления", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_Marked" + }; + property.PropertyType.CanBeBoolean = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 1, + TypeName = "binary" + }); + metaObject.Properties.Add(property); + } + public void ConfigurePropertyПредопределённый(ApplicationObject metaObject) + { + MetadataProperty property = new MetadataProperty() + { + Name = "Предопределённый", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_PredefinedID" + }; + property.PropertyType.IsUuid = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 16, + TypeName = "binary" + }); + metaObject.Properties.Add(property); + } + public void ConfigurePropertyКод(ApplicationObject metaObject) + { + if (!(metaObject is IReferenceCode code)) throw new ArgumentOutOfRangeException(); + + MetadataProperty property = new MetadataProperty() + { + Name = "Код", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_Code" + }; + if (code.CodeType == CodeType.String) + { + property.PropertyType.CanBeString = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = code.CodeLength, + TypeName = "nvarchar" + }); + } + else + { + property.PropertyType.CanBeNumeric = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Precision = code.CodeLength, + TypeName = "numeric" + }); + } + metaObject.Properties.Add(property); + } + public void ConfigurePropertyНаименование(ApplicationObject metaObject) + { + if (!(metaObject is IDescription description)) throw new ArgumentOutOfRangeException(); + + MetadataProperty property = new MetadataProperty() + { + Name = "Наименование", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_Description" + }; + property.PropertyType.CanBeString = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = description.DescriptionLength, + TypeName = "nvarchar" + }); + metaObject.Properties.Add(property); + } + public void ConfigurePropertyРодитель(ApplicationObject metaObject) + { + MetadataProperty property = new MetadataProperty() + { + Name = "Родитель", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_ParentIDRRef" + }; + property.PropertyType.CanBeReference = true; + property.PropertyType.ReferenceTypeUuid = metaObject.Uuid; + property.PropertyType.ReferenceTypeCode = metaObject.TypeCode; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 16, + TypeName = "binary" + }); + metaObject.Properties.Add(property); + } + public void ConfigurePropertyЭтоГруппа(ApplicationObject metaObject) + { + MetadataProperty property = new MetadataProperty() + { + Name = "ЭтоГруппа", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_Folder" + }; + property.PropertyType.CanBeBoolean = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 1, + TypeName = "binary" + }); + metaObject.Properties.Add(property); + } + public void ConfigurePropertyВладелец(Catalog catalog, Guid owner) + { + MetadataProperty property = new MetadataProperty + { + Name = "Владелец", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_OwnerID" + }; + property.PropertyType.CanBeReference = true; + + if (catalog.Owners == 1) // Single type value + { + property.PropertyType.ReferenceTypeUuid = owner; + property.Fields.Add(new DatabaseField() + { + Name = "_OwnerIDRRef", + Length = 16, + TypeName = "binary" + }); + } + else // Multiple type value + { + property.Fields.Add(new DatabaseField() + { + Name = "_OwnerID_TYPE", + Length = 1, + TypeName = "binary", + Purpose = FieldPurpose.Discriminator + }); + property.Fields.Add(new DatabaseField() + { + Name = "_OwnerID_RTRef", + Length = 4, + TypeName = "binary", + Purpose = FieldPurpose.TypeCode + }); + property.Fields.Add(new DatabaseField() + { + Name = "_OwnerID_RRRef", + Length = 16, + TypeName = "binary", + Purpose = FieldPurpose.Object + }); + } + + catalog.Properties.Add(property); + } + + #endregion + + #region "Characteristic properties" + + public void ConfigurePropertyТипЗначения(Characteristic characteristic) + { + MetadataProperty property = new MetadataProperty() + { + Name = "ТипЗначения", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_Type" + }; + property.PropertyType.IsBinary = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = -1, + IsNullable = true, + TypeName = "varbinary" + }); + characteristic.Properties.Add(property); + } + + #endregion + + #region "Document system properties" + + public void ConfigurePropertyДата(Document document) + { + MetadataProperty property = new MetadataProperty() + { + Name = "Дата", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_Date_Time" + }; + property.PropertyType.CanBeDateTime = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 6, + Precision = 19, + TypeName = "datetime2" + }); + document.Properties.Add(property); + } + public void ConfigurePropertyПериодичность(Document document) + { + MetadataProperty property = new MetadataProperty() + { + Name = "ПериодНомера", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_NumberPrefix" + }; + property.PropertyType.CanBeDateTime = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 6, + Precision = 19, + TypeName = "datetime2" + }); + document.Properties.Add(property); + } + public void ConfigurePropertyПроведён(Document document) + { + MetadataProperty property = new MetadataProperty() + { + Name = "Проведён", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_Posted" + }; + property.PropertyType.CanBeBoolean = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 1, + TypeName = "binary" + }); + document.Properties.Add(property); + } + public void ConfigurePropertyНомер(Document document) + { + MetadataProperty property = new MetadataProperty() + { + Name = "Номер", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_Number" + }; + if (document.NumberType == NumberType.Number) + { + property.PropertyType.CanBeNumeric = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 6, + Precision = 19, + TypeName = "numeric" + }); + } + else + { + property.PropertyType.CanBeString = true; + property.PropertyType.StringKind = StringKind.Fixed; + property.PropertyType.StringLength = document.NumberLength; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = document.NumberLength, + TypeName = "nvarchar" + }); + } + document.Properties.Add(property); + } + + // Используется для сихронизации добавления свойства "Регистратор" между документами + private readonly object _syncRegister = new object(); + public void ConfigurePropertyРегистратор(ApplicationObject register, Document document) + { + lock (_syncRegister) + { + ConfigurePropertyРегистраторSynchronized(register, document); + } + } + private void ConfigurePropertyРегистраторSynchronized(ApplicationObject register, Document document) + { + MetadataProperty property = register.Properties.Where(p => p.Name == "Регистратор").FirstOrDefault(); + + if (property == null) + { + // добавляем новое свойство + property = new MetadataProperty() + { + Name = "Регистратор", + Purpose = PropertyPurpose.System, + FileName = Guid.Empty, + DbName = "_Recorder" + }; + property.PropertyType.CanBeReference = true; + property.PropertyType.ReferenceTypeUuid = document.Uuid; // single type value + property.PropertyType.ReferenceTypeCode = document.TypeCode; // single type value + property.Fields.Add(new DatabaseField() + { + Name = "_RecorderRRef", + Length = 16, + TypeName = "binary", + Scale = 0, + Precision = 0, + IsNullable = false, + KeyOrdinal = 0, + IsPrimaryKey = true, + Purpose = FieldPurpose.Value + }); + register.Properties.Add(property); + return; + } + + // На всякий случай проверям повторное обращение одного и того же документа + if (property.PropertyType.ReferenceTypeUuid == document.Uuid) return; + + // Проверям необходимость добавления поля для хранения кода типа документа + if (property.PropertyType.ReferenceTypeUuid == Guid.Empty) return; + + // Изменяем назначение поля для хранения ссылки на документ, предварительно убеждаясь в его наличии + DatabaseField field = property.Fields.Where(f => f.Name.ToLowerInvariant() == "_recorderrref").FirstOrDefault(); + if (field != null) + { + field.Purpose = FieldPurpose.Object; + } + + // Добавляем поле для хранения кода типа документа, предварительно убеждаясь в его отсутствии + if (property.Fields.Where(f => f.Name.ToLowerInvariant() == "_recordertref").FirstOrDefault() == null) + { + property.Fields.Add(new DatabaseField() + { + Name = "_RecorderTRef", + Length = 4, + TypeName = "binary", + Scale = 0, + Precision = 0, + IsNullable = false, + KeyOrdinal = 0, + IsPrimaryKey = true, + Purpose = FieldPurpose.TypeCode + }); + } + + // Устанавливаем признак множественного типа значения (составного типа данных) + property.PropertyType.ReferenceTypeCode = 0; // multiple type value + property.PropertyType.ReferenceTypeUuid = Guid.Empty; // multiple type value + } + public void ConfigureRegistersToPost(Document document, ConfigObject registers) + { + int registersCount = registers.GetInt32(new[] { 1 }); // количество регистров + if (registersCount == 0) return; + + //int offset = 2; + for (int r = 0; r < registersCount; r++) + { + // R.2.1 - uuid файла регистра + //Guid fileName = registers.GetUuid(new[] { r + offset, 2, 1 }); + foreach (var collection in InfoBase.Registers) + { + if (collection.TryGetValue(document.FullKey, out ApplicationObject register)) + { + ConfigurePropertyРегистратор(register, document); + break; + } + } + } + } + + #endregion + + #region "Publication system properties" + + public void ConfigurePropertyНомерОтправленного(Publication publication) + { + MetadataProperty property = new MetadataProperty() + { + Name = "НомерОтправленного", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_SentNo" + }; + property.PropertyType.CanBeNumeric = true; + property.PropertyType.NumericScale = 0; + property.PropertyType.NumericPrecision = 10; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 9, + Scale = 0, + Precision = 10, + TypeName = "numeric" + }); + publication.Properties.Add(property); + } + public void ConfigurePropertyНомерПринятого(Publication publication) + { + MetadataProperty property = new MetadataProperty() + { + Name = "НомерПринятого", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_ReceivedNo" + }; + property.PropertyType.CanBeNumeric = true; + property.PropertyType.NumericScale = 0; + property.PropertyType.NumericPrecision = 10; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 9, + Scale = 0, + Precision = 10, + TypeName = "numeric" + }); + publication.Properties.Add(property); + } + + #endregion + + #region "Enumeration system properties" + + public void ConfigurePropertyПорядок(Enumeration enumeration) + { + MetadataProperty property = new MetadataProperty() + { + Name = "Порядок", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_EnumOrder" + }; + property.PropertyType.CanBeNumeric = true; + property.PropertyType.NumericScale = 0; + property.PropertyType.NumericPrecision = 10; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 9, + Scale = 0, + Precision = 10, + TypeName = "numeric" + }); + enumeration.Properties.Add(property); + } + + #endregion + + #region "Information register system properties" + + public void ConfigurePropertyПериод(ApplicationObject register) + { + MetadataProperty property = new MetadataProperty() + { + Name = "Период", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_Period" + }; + property.PropertyType.CanBeDateTime = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 6, + Precision = 19, + TypeName = "datetime2" + }); + register.Properties.Add(property); + } + public void ConfigurePropertyНомерЗаписи(ApplicationObject register) + { + MetadataProperty property = new MetadataProperty() + { + Name = "НомерСтроки", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_LineNo" + }; + property.PropertyType.CanBeNumeric = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 5, + Precision = 9, + TypeName = "numeric" + }); + register.Properties.Add(property); + } + public void ConfigurePropertyАктивность(ApplicationObject register) + { + MetadataProperty property = new MetadataProperty() + { + Name = "Активность", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_Active" + }; + property.PropertyType.CanBeBoolean = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 1, + TypeName = "binary" + }); + register.Properties.Add(property); + } + + #endregion + + #region "Accumulation register system properties" + + public void ConfigurePropertyВидДвижения(AccumulationRegister register) + { + MetadataProperty property = new MetadataProperty() + { + Name = "ВидДвижения", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_RecordKind" + }; + property.PropertyType.CanBeNumeric = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 5, + Precision = 1, + TypeName = "numeric" + }); + register.Properties.Add(property); + } + public void ConfigurePropertyDimHash(AccumulationRegister register) + { + MetadataProperty property = new MetadataProperty() + { + Name = "DimHash", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_DimHash" + }; + property.PropertyType.CanBeNumeric = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 9, + Precision = 10, + TypeName = "numeric" + }); + register.Properties.Add(property); + } + + #endregion + + #region "Accounts system properties" + + public void ConfigurePropertyПорядок(Account metaObject) + { + MetadataProperty property = new MetadataProperty() + { + Name = "Порядок", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_OrderField" + }; + property.PropertyType.CanBeString = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 7, + TypeName = "nvarchar" + }); + metaObject.Properties.Add(property); + } + + public void ConfigurePropertyВид(Account metaObject) + { + MetadataProperty property = new MetadataProperty() + { + Name = "Вид", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_Kind" + }; + property.PropertyType.CanBeNumeric = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 1, + Precision = 0, + TypeName = "numeric" + }); + metaObject.Properties.Add(property); + } + + public void ConfigurePropertyЗабалансовый(Account metaObject) + { + MetadataProperty property = new MetadataProperty() + { + Name = "Забалансовый", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_OffBalance" + }; + property.PropertyType.IsBinary = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 1, + TypeName = "binary" + }); + metaObject.Properties.Add(property); + } + + #endregion + + #region "Accounting register system properties" + + public void ConfigurePropertyСчетДт(AccountingRegister register) + { + MetadataProperty property = new MetadataProperty() + { + Name = "СчетДт", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_AccountDtRRef" + }; + property.PropertyType.IsBinary = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 16, + TypeName = "binary" + }); + register.Properties.Add(property); + } + + public void ConfigurePropertyСчетКт(AccountingRegister register) + { + MetadataProperty property = new MetadataProperty() + { + Name = "СчетКт", + FileName = Guid.Empty, + Purpose = PropertyPurpose.System, + DbName = "_AccountCtRRef" + }; + property.PropertyType.IsBinary = true; + property.Fields.Add(new DatabaseField() + { + Name = property.DbName, + Length = 16, + TypeName = "binary" + }); + register.Properties.Add(property); + } + + #endregion + + #region "Predefined values (catalogs and characteristics)" + + public void ConfigurePredefinedValues(ApplicationObject metaObject) + { + if (!(metaObject is IPredefinedValues owner)) return; + + int predefinedValueUuid = 3; + int predefinedIsFolder = 4; + int predefinedValueName = 6; + int predefinedValueCode = 7; + int predefinedDescription = 8; + + string fileName = metaObject.FileName.ToString() + ".1c"; // файл с описанием предопределённых элементов + if (metaObject is Characteristic) + { + fileName = metaObject.FileName.ToString() + ".7"; + predefinedValueName = 5; + predefinedValueCode = 6; + predefinedDescription = 7; + } + + IReferenceCode codeInfo = (metaObject as IReferenceCode); + + ConfigObject configObject = FileReader.ReadConfigObject(fileName); + + if (configObject == null) return; + + ConfigObject parentObject = configObject.GetObject(new[] { 1, 2, 14, 2 }); + + //string RootName = parentObject.GetString(new int[] { 6, 1 }); // имя корня предопределённых элементов = "Элементы" (уровень 0) + //string RootName = parentObject.GetString(new int[] { 5, 1 }); // имя корня предопределённых элементов = "Характеристики" (уровень 0) + + int propertiesCount = parentObject.GetInt32(new[] { 2 }); + int predefinedFlag = propertiesCount + 3; + int childrenValues = propertiesCount + 4; + + int hasChildren = parentObject.GetInt32(new[] { predefinedFlag }); // флаг наличия предопределённых элементов + if (hasChildren == 0) return; + + ConfigObject predefinedValues = parentObject.GetObject(new[] { childrenValues }); // коллекция описаний предопределённых элементов + + int valuesCount = predefinedValues.GetInt32(new[] { 1 }); // количество предопределённых элементов (уровень 1) + + if (valuesCount == 0) return; + + int valueOffset = 2; + for (int v = 0; v < valuesCount; v++) + { + PredefinedValue pv = new PredefinedValue(); + + ConfigObject predefinedValue = predefinedValues.GetObject(new[] { v + valueOffset }); + + pv.Uuid = predefinedValue.GetUuid(new[] { predefinedValueUuid, 2, 1 }); + pv.Name = predefinedValue.GetString(new[] { predefinedValueName, 1 }); + pv.IsFolder = (predefinedValue.GetInt32(new[] { predefinedIsFolder, 1 }) == 1); + pv.Description = predefinedValue.GetString(new[] { predefinedDescription, 1 }); + + if (codeInfo != null && codeInfo.CodeLength > 0) + { + pv.Code = predefinedValue.GetString(new[] { predefinedValueCode, 1 }); + } + + owner.PredefinedValues.Add(pv); + + int haveChildren = predefinedValue.GetInt32(new[] { 9 }); // флаг наличия дочерних предопределённых элементов (0 - нет, 1 - есть) + if (haveChildren == 1) + { + ConfigObject children = predefinedValue.GetObject(new[] { 10 }); // коллекция описаний дочерних предопределённых элементов + + ConfigurePredefinedValue(children, pv, metaObject); + } + } + } + private void ConfigurePredefinedValue(ConfigObject predefinedValues, PredefinedValue parent, ApplicationObject owner) + { + int valuesCount = predefinedValues.GetInt32(new[] { 1 }); // количество предопределённых элементов (уровень N) + + if (valuesCount == 0) return; + + int predefinedValueUuid = 3; + int predefinedIsFolder = 4; + int predefinedValueName = 6; + int predefinedValueCode = 7; + int predefinedDescription = 8; + + if (owner is Characteristic) + { + predefinedValueName = 5; + predefinedValueCode = 6; + predefinedDescription = 7; + } + + IReferenceCode codeInfo = (owner as IReferenceCode); + + int valueOffset = 2; + for (int v = 0; v < valuesCount; v++) + { + PredefinedValue pv = new PredefinedValue(); + + ConfigObject predefinedValue = predefinedValues.GetObject(new[] { v + valueOffset }); + + pv.Uuid = predefinedValue.GetUuid(new[] { predefinedValueUuid, 2, 1 }); + pv.Name = predefinedValue.GetString(new[] { predefinedValueName, 1 }); + pv.IsFolder = (predefinedValue.GetInt32(new[] { predefinedIsFolder, 1 }) == 1); + pv.Description = predefinedValue.GetString(new[] { predefinedDescription, 1 }); + + if (codeInfo != null && codeInfo.CodeLength > 0) + { + pv.Code = predefinedValue.GetString(new[] { predefinedValueCode, 1 }); + } + + parent.Children.Add(pv); + + int haveChildren = predefinedValue.GetInt32(new[] { 9 }); // флаг наличия дочерних предопределённых элементов (0 - нет, 1 - есть) + if (haveChildren == 1) + { + ConfigObject children = predefinedValue.GetObject(new[] { 10 }); // коллекция описаний дочерних предопределённых элементов + + ConfigurePredefinedValue(children, pv, owner); + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/IMetadataService.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/IMetadataService.cs new file mode 100644 index 0000000..f75c660 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/IMetadataService.cs @@ -0,0 +1,43 @@ +using System.IO; +using YPermitin.SQLCLR.YellowMetadataReader.Models; +using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Services +{ + /// + /// Интерфейс для чтения метаданных прикладных объектов конфигурации 1С. + /// Является точкой входа для использования библиотеки и играет роль фасада для всех остальных интерфейсов и парсеров. + /// Реализует логику многопоточной загрузки объектов конфигурации 1С. + /// + public interface IMetadataService + { + ///Строка подключения к базе данных СУБД + string ConnectionString { get; } + + ///Устанавливает строку подключения к базе данных СУБД + ///Строка подключения к базе данных СУБД + ///Возвращает ссылку на самого себя + IMetadataService UseConnectionString(string connectionString); + IMetadataService UseDatabaseName(string databaseName); + + ///Формирует строку подключения к базе данных по параметрам + ///Имя или сетевой адрес сервера СУБД + ///Имя базы данных + ///Имя пользователя (если не указано, то используется Windows аутентификация) + ///Пароль пользователя (используется в случае аутентификации средствами СУБД) + ///Возвращает ссылку на самого себя + IMetadataService ConfigureConnectionString(string server, string database, string userName, string password); + + InfoBase OpenInfoBase(OpenInfobaseLevel level = OpenInfobaseLevel.ConfigFull); + + ///Получает файл метаданных в "сыром" (как есть) бинарном виде + ///Имя файла метаданных: root, DBNames или значение UUID + ///Бинарные данные файла метаданных + byte[] ReadConfigFile(string fileName); + + ///Распаковывает файл метаданных по алгоритму deflate и создаёт поток для чтения в формате UTF-8 + ///Бинарные данные файла метаданных + ///Поток для чтения файла метаданных в формате UTF-8 + StreamReader CreateReader(byte[] fileData); + } +} diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/ISqlMetadataReader.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/ISqlMetadataReader.cs new file mode 100644 index 0000000..d680848 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/ISqlMetadataReader.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using YPermitin.SQLCLR.YellowMetadataReader.Models; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Services +{ + public interface ISqlMetadataReader + { + string ConnectionString { get; } + string DatabaseName { get; } + void UseConnectionString(string connectionString); + void UseDatabaseName(string databaseName); + + void ConfigureConnectionString(string server, string database, string userName, string password); + List GetSqlFieldsOrderedByName(string tableName); + } +} diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/InternalFormatReader.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/InternalFormatReader.cs new file mode 100644 index 0000000..988de05 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/InternalFormatReader.cs @@ -0,0 +1,62 @@ +using System; +using System.IO; +using System.IO.Compression; +using System.Text; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Services +{ + public static class InternalFormatReader + { + public static string ParseToString(byte[] data) + { + string parsedValue; + + using (StreamReader stream = CreateReader(data)) + { + parsedValue = ParseToString(stream); + } + + return parsedValue; + } + + private static string ParseToString(StreamReader stream) + { + return stream.ReadToEnd(); + } + + private static StreamReader CreateReader(byte[] data) + { + if (IsUTF8(data)) + { + return CreateStreamReader(data); + } + return CreateDeflateReader(data); + } + + private static StreamReader CreateStreamReader(byte[] data) + { + MemoryStream memory = new MemoryStream(data); + return new StreamReader(memory, Encoding.UTF8); + } + + private static StreamReader CreateDeflateReader(byte[] data) + { + MemoryStream memory = new MemoryStream(data); + DeflateStream stream = new DeflateStream(memory, CompressionMode.Decompress); + return new StreamReader(stream, Encoding.UTF8); + } + + private static bool IsUTF8(byte[] fileData) + { + if (fileData == null) + throw new ArgumentNullException(nameof(fileData)); + + if (fileData.Length < 3) + return false; + + return fileData[0] == 0xEF + && fileData[1] == 0xBB + && fileData[2] == 0xBF; + } + } +} diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/MetadataService.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/MetadataService.cs new file mode 100644 index 0000000..a7f1afd --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/MetadataService.cs @@ -0,0 +1,66 @@ +using System.IO; +using YPermitin.SQLCLR.YellowMetadataReader.Models; +using YPermitin.SQLCLR.YellowMetadataReader.Models.Enums; + +namespace YPermitin.SQLCLR.YellowMetadataReader.Services +{ + public class MetadataService : IMetadataService + { + private readonly IConfigFileReader _configFileReader; + private readonly ISqlMetadataReader _sqlMetadataReader; + + public string ConnectionString { get; private set; } = string.Empty; + public string DatabaseName { get; private set; } = string.Empty; + + public MetadataService() + { + _configFileReader = new ConfigFileReader(); + _sqlMetadataReader = new SqlMetadataReader(); + } + + public IMetadataService UseConnectionString(string connectionString) + { + ConnectionString = connectionString; + _sqlMetadataReader.UseConnectionString(ConnectionString); + _configFileReader.UseConnectionString(ConnectionString); + return this; + } + + public IMetadataService UseDatabaseName(string databaseName) + { + DatabaseName = databaseName; + + _sqlMetadataReader.UseConnectionString(ConnectionString); + _sqlMetadataReader.UseDatabaseName(DatabaseName); + + _configFileReader.UseConnectionString(ConnectionString); + _configFileReader.UseDatabaseName(DatabaseName); + + return this; + } + + public IMetadataService ConfigureConnectionString(string server, string database, string userName, string password) + { + _configFileReader.ConfigureConnectionString(server, database, userName, password); + ConnectionString = _configFileReader.ConnectionString; + _sqlMetadataReader.UseConnectionString(ConnectionString); + return this; + } + + public InfoBase OpenInfoBase(OpenInfobaseLevel level = OpenInfobaseLevel.ConfigFull) + { + Configurator configurator = new Configurator(_configFileReader, _sqlMetadataReader); + return configurator.OpenInfoBase(level); + } + + public byte[] ReadConfigFile(string fileName) + { + return _configFileReader.ReadBytes(fileName); + } + + public StreamReader CreateReader(byte[] fileData) + { + return _configFileReader.CreateDeflateReader(fileData); + } + } +} diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/SqlMetadataReader.cs b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/SqlMetadataReader.cs new file mode 100644 index 0000000..8127ef9 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/Services/SqlMetadataReader.cs @@ -0,0 +1,241 @@ +using System.Collections.Generic; +using System.Data.SqlClient; +using System.Text; +using YPermitin.SQLCLR.YellowMetadataReader.Models; +// ReSharper disable NotAccessedField.Local +// ReSharper disable InconsistentNaming + +namespace YPermitin.SQLCLR.YellowMetadataReader.Services +{ + public class SqlMetadataReader : ISqlMetadataReader + { + private sealed class ClusteredIndexInfo + { + // ReSharper disable once EmptyConstructor + public ClusteredIndexInfo() { } + public string NAME; + public bool IS_UNIQUE; + public bool IS_PRIMARY_KEY; + public readonly List COLUMNS = new List(); + public bool HasNullableColumns + { + get + { + foreach (ClusteredIndexColumnInfo item in COLUMNS) + { + if (item.IS_NULLABLE) + { + return true; + } + } + return false; + } + } + public ClusteredIndexColumnInfo GetColumnByName(string name) + { + for (int i = 0; i < COLUMNS.Count; i++) + { + if (COLUMNS[i].NAME == name) return COLUMNS[i]; + } + return null; + } + } + private sealed class ClusteredIndexColumnInfo + { + public ClusteredIndexColumnInfo() { } + public byte KEY_ORDINAL; + public string NAME; + public bool IS_NULLABLE; + } + public string ConnectionString { get; private set; } = string.Empty; + public string DatabaseName { get; private set; } = string.Empty; + public void UseConnectionString(string connectionString) + { + ConnectionString = connectionString; + } + public void UseDatabaseName(string databaseName) + { + DatabaseName = databaseName; + } + public void ConfigureConnectionString(string server, string database, string userName, string password) + { + ConfigureConnectionStringForSQLServer(server, database, userName, password); + } + private void ConfigureConnectionStringForSQLServer(string server, string database, string userName, string password) + { + SqlConnectionStringBuilder connectionString = new SqlConnectionStringBuilder() + { + DataSource = server, + InitialCatalog = database + }; + if (!string.IsNullOrWhiteSpace(userName)) + { + connectionString.UserID = userName; + connectionString.Password = password; + } + connectionString.IntegratedSecurity = string.IsNullOrWhiteSpace(userName); + ConnectionString = connectionString.ToString(); + } + + + private List GetSqlFields(string tableName) + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine(@"SELECT"); + sb.AppendLine(@" ORDINAL_POSITION, COLUMN_NAME, DATA_TYPE,"); + sb.AppendLine(@" ISNULL(CHARACTER_MAXIMUM_LENGTH, 0) AS CHARACTER_MAXIMUM_LENGTH,"); + sb.AppendLine(@" ISNULL(NUMERIC_PRECISION, 0) AS NUMERIC_PRECISION,"); + sb.AppendLine(@" ISNULL(NUMERIC_SCALE, 0) AS NUMERIC_SCALE,"); + sb.AppendLine(@" CASE WHEN IS_NULLABLE = 'NO' THEN CAST(0x00 AS bit) ELSE CAST(0x01 AS bit) END AS IS_NULLABLE"); + sb.AppendLine(@"FROM"); + sb.AppendLine(@" [{DatabaseName}].[INFORMATION_SCHEMA].[COLUMNS]"); + sb.AppendLine(@"WHERE"); + sb.AppendLine(@" TABLE_NAME = N'{0}'"); + sb.AppendLine(@"ORDER BY"); + sb.AppendLine(@" ORDINAL_POSITION ASC;"); + + string sql = string.Format(sb.ToString(), tableName); + + List list = new List(); + using (SqlConnection connection = new SqlConnection(ConnectionString)) + { + using (SqlCommand command = new SqlCommand(sql.Replace("{DatabaseName}", DatabaseName), connection)) + { + connection.Open(); + using (SqlDataReader reader = command.ExecuteReader()) + { + while (reader.Read()) + { + SqlFieldInfo item = new SqlFieldInfo() + { + ORDINAL_POSITION = reader.GetInt32(0), + COLUMN_NAME = reader.GetString(1), + DATA_TYPE = reader.GetString(2), + CHARACTER_MAXIMUM_LENGTH = reader.GetInt32(3), + NUMERIC_PRECISION = reader.GetByte(4), + NUMERIC_SCALE = reader.GetByte(5), + IS_NULLABLE = reader.GetBoolean(6) + }; + list.Add(item); + } + } + } + } + return list; + } + private ClusteredIndexInfo GetClusteredIndexInfo(string tableName) + { + ClusteredIndexInfo info = null; + + StringBuilder sb = new StringBuilder(); + sb.AppendLine(@"SELECT"); + sb.AppendLine(@" i.name,"); + sb.AppendLine(@" i.is_unique,"); + sb.AppendLine(@" i.is_primary_key,"); + sb.AppendLine(@" c.key_ordinal,"); + sb.AppendLine(@" f.name,"); + sb.AppendLine(@" f.is_nullable"); + sb.AppendLine(@"FROM [{DatabaseName}].[sys].[indexes] AS i"); + sb.AppendLine(@"INNER JOIN [{DatabaseName}].[sys].[tables] AS t ON t.object_id = i.object_id"); + sb.AppendLine(@"INNER JOIN [{DatabaseName}].[sys].[index_columns] AS c ON c.object_id = t.object_id AND c.index_id = i.index_id"); + sb.AppendLine(@"INNER JOIN [{DatabaseName}].[sys].[columns] AS f ON f.object_id = t.object_id AND f.column_id = c.column_id"); + sb.AppendLine(@"WHERE"); + sb.AppendLine(@" t.object_id = OBJECT_ID(@table) AND i.type = 1 -- CLUSTERED"); + sb.AppendLine(@"ORDER BY"); + sb.AppendLine(@"c.key_ordinal ASC;"); + string sql = sb.ToString().Replace("{DatabaseName}", DatabaseName); + + using (SqlConnection connection = new SqlConnection(ConnectionString)) + using (SqlCommand command = new SqlCommand(sql, connection)) + { + connection.Open(); + + command.Parameters.AddWithValue("table", tableName); + using (SqlDataReader reader = command.ExecuteReader()) + { + if (reader.Read()) + { + info = new ClusteredIndexInfo() + { + NAME = reader.GetString(0), + IS_UNIQUE = reader.GetBoolean(1), + IS_PRIMARY_KEY = reader.GetBoolean(2) + }; + info.COLUMNS.Add(new ClusteredIndexColumnInfo() + { + KEY_ORDINAL = reader.GetByte(3), + NAME = reader.GetString(4), + IS_NULLABLE = reader.GetBoolean(5) + }); + while (reader.Read()) + { + info.COLUMNS.Add(new ClusteredIndexColumnInfo() + { + KEY_ORDINAL = reader.GetByte(3), + NAME = reader.GetString(4), + IS_NULLABLE = reader.GetBoolean(5) + }); + } + } + } + } + return info; + } + + private string SelectSqlFieldsOrderedByNameScript() + { + return MS_SelectSqlFieldsOrderedByNameScript(); + } + private string MS_SelectSqlFieldsOrderedByNameScript() + { + StringBuilder script = new StringBuilder(); + script.AppendLine("SELECT"); + script.AppendLine("c.column_id AS ORDINAL_POSITION,"); + script.AppendLine("c.name AS COLUMN_NAME,"); + script.AppendLine("s.name AS DATA_TYPE,"); + script.AppendLine("c.max_length AS CHARACTER_OCTET_LENGTH,"); + script.AppendLine("c.max_length AS CHARACTER_MAXIMUM_LENGTH,"); // TODO: for nchar and nvarchar devide by 2 + script.AppendLine("c.precision AS NUMERIC_PRECISION,"); + script.AppendLine("c.scale AS NUMERIC_SCALE,"); + script.AppendLine("c.is_nullable AS IS_NULLABLE"); + script.AppendLine("FROM [{DatabaseName}].[sys].[tables] AS t"); + script.AppendLine("INNER JOIN [{DatabaseName}].[sys].[columns] AS c ON c.object_id = t.object_id"); + script.AppendLine("INNER JOIN [{DatabaseName}].[sys].[types] AS s ON c.user_type_id = s.user_type_id"); + script.AppendLine("WHERE t.object_id = OBJECT_ID(@tableName)"); + script.AppendLine("ORDER BY c.name ASC;"); + return script.ToString().Replace("{DatabaseName}", DatabaseName); + } + + public List GetSqlFieldsOrderedByName(string tableName) + { + return MS_GetSqlFieldsOrderedByName(tableName); + } + private List MS_GetSqlFieldsOrderedByName(string tableName) + { + List list = new List(); + using (SqlConnection connection = new SqlConnection(ConnectionString)) + using (SqlCommand command = new SqlCommand(SelectSqlFieldsOrderedByNameScript(), connection)) + { + command.Parameters.AddWithValue("tableName", $"{DatabaseName}.dbo.{tableName}"); + connection.Open(); + using (SqlDataReader reader = command.ExecuteReader()) + { + while (reader.Read()) + { + SqlFieldInfo item = new SqlFieldInfo(); + item.ORDINAL_POSITION = reader.GetInt32(0); + item.COLUMN_NAME = reader.GetString(1); + item.DATA_TYPE = reader.GetString(2); + item.CHARACTER_OCTET_LENGTH = reader.GetInt16(3); + item.CHARACTER_MAXIMUM_LENGTH = reader.GetInt16(4); + item.NUMERIC_PRECISION = reader.GetByte(5); + item.NUMERIC_SCALE = reader.GetByte(6); + item.IS_NULLABLE = reader.GetBoolean(7); + list.Add(item); + } + } + } + return list; + } + } +} diff --git a/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/YellowMetadataReader.csproj b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/YellowMetadataReader.csproj new file mode 100644 index 0000000..fb13d07 --- /dev/null +++ b/SQL-Server-SQLCLR/Projects/YellowMetadataReader/YellowMetadataReader/YellowMetadataReader.csproj @@ -0,0 +1,128 @@ + + + + + Debug + AnyCPU + {C4503533-BFBD-4E7E-A00E-941746748DDD} + Library + Properties + YPermitin.SQLCLR.YellowMetadataReader + YellowMetadataReader + v4.8 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file