Visual Studio + Roslyn = конфігуратор для 1С: Підприємство

Roslyn дозволяє C# проект перетворити на відкритий XML-формат конфігурації 1С: Підприємство. C#-проект за підтримки Visual Studio автоматично забезпечується Intellisense, інтерактивною перевіркою синтаксису і типів, рефакторінгом, розширеним пошуком за проектом, підтримкою XmlDoc. Переходів розташування документів проекту на диску і більш виразний і економний формат робить C#-проект Visual Studio кращим вибором у системах версионирования.

Зрозуміло, що від чистої теорії до реалізації всіх особливостей 1С дуже далеко. Наведений у статті приклад володіє наступними обмеженнями. У прикладі реалізована підтримка декількох типів об'єктів і декількох часто зустрічаються властивостей. Атрибути об'єктів можуть бути одного типу, хоча 1С допускає складовою тип. Трансляція коду в код 1С не підтримується. Реалізовані англомовні найменування.



XML-вивантаження конфігурації 1С

Починаючи з версії 8.3 1С навчилася вивантажувати завантажити конфігурацію з відкритий формат XML. Робить вона це своєю самобутньою манерою. Вивантаження, наприклад, УТ 11.0.7 займе приблизно 6 хвилин. При цьому всі файли конфігурації будуть повалені в єдиний каталог. Файлів буде близько 10K (десяти тисяч) розміром приблизно 430 Мбайт. Така вивантаження вважається величезним досягненням в області відкритості, тому що раніше конфігурація запаковувалися в закритий cf-формат, розпізнати який можна було тільки спеціалізованими засобами: через .Net-сумісний cfProject або v8unpack.

Так виглядає один з файлів XML Language.Російська.xml

<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ua/8.3/MDClasses" xmlns:app="http://v8.1c.ua/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ua/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ua/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ua/8.1/data/enterprise" xmlns:lf="http://v8.1c.ua/8.2/managed-application/logform" xmlns:style="http://v8.1c.ua/8.1/data/ui/style" xmlns:sys="http://v8.1c.ua/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ua/8.1/data/core" xmlns:v8ui="http://v8.1c.ua/8.1/data/ui" xmlns:web="http://v8.1c.ua/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ua/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ua/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ua/8.3/xcf/predef" xmlns:xr="http://v8.1c.ua/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Language uuid="7a630662-cf14-44d7-a01c-08006bb5bffa">
<Properties>
<Name>Російський</Name>
<Synonym/>
<Comment/>
<LanguageCode>ru</LanguageCode>
</Properties>
</Language>
</MetaDataObject>


Заміна XML-конструкцій на визначення C#

У проекті C# за кожен об'єкт 1С відповідає клас. Якщо це довідник, то клас успадковується від класу Catalog, перерахування успадковується від класу Enum. Властивості об'єктів і атрибутів позначаються через C#-атрибути.

В результаті XML-визначення мова Російська прийме такий вигляд Російська.cs

using System;
using Elisy.Configuration.DataAnnotations;
using Elisy.Configuration;
namespace Test.Languages
{
[Uuid("7a630662-cf14-44d7-a01c-08006bb5bffa")]
[LanguageCode("ru")]
public class Російська : Language
{
}
}


Посилання на об'єкти визначаються через типи виду CatalogRef<>. Наприклад, так:

[Uuid("e140b824-c8f9-4155-87e6-d408e73ccc69")]
[Synonym("ru", "Рубрика")]
public CatalogRef<рубрики> Рубрика;


Таблична частина визначається як TabularSection<строкатабличнойчасти>

[Uuid("bb2c088e-8fb3-4469-abd7-ba6b4104ae88")]
[GeneratedTypeAttribute(GeneratedTypeCategory.TabularSection, "8ef1c35e-8b55-488f-8e24-61f73d287458", "b300dc36-fe12-41d5-a462-792716a1e508")]
[GeneratedTypeAttribute(GeneratedTypeCategory.TabularSectionRow, "92e3cead-a61b-474c-8515-56e4911339c7", "dfa007ab-4185-443f-8d11-f7468a7c195e")]
[Synonym("ru", "Рубрики")]
public TabularSection<РубрикаИзображения> Рубрики;


Події модуля об'єкта і модуля менеджера реалізуються як override-методи. Можуть виглядати так:

public override void BeforeWrite(ref bool cancel)
{
var ДубликатПоНаименованию = Catalog.FindByDescription<Зображення>(Description, true);
if (!IsFolder && IsNew() && G. ValueIsFilled(ДубликатПоНаименованию))
{
G. Message("Знайдений дублікат зображення з найменуванням: " + Description + ". Запис не здійснюється");
cancel = true;
return;
}
if (!IsFolder)
Рубрики.GroupBy("Рубрика");

//Прибрати непотрібні рубрики, оброблені груповий обробкою
var НайденнаяСтрока = Рубрики.Find(Catalogs.Рубрики.УдалитьРубрику, "Рубрика");
if (G. ValueIsFilled(НайденнаяСтрока))
Рубрики.Delete(НайденнаяСтрока);
}


Слід зазначити, що методи зараз ніяк не транслюються в код 1С. Не зрозумілим залишається, що робити з директивами препроцесора 1С і потрібно множинне успадкування основних об'єктів 1С. G-позначає глобальний контекст, так як виклик функцій в C# може виконуватися тільки в прив'язці до класу.

Складання проекту CSPROJ

У минулій статті habrahabr.ru/post/245453/ була описана можливість компілювання одного файлу CS. Частіше потрібно скомпілювати цілий проект — кілька файлів.

У Roslyn є вбудовані класи, що відповідають за обробку sln-рішень і csproj-проектів Visual Studio. Проблема полягає в тому, що вони посилаються на окремі збірки, що поставляються в складі MSBuild Tools. Остання версія Roslyn посилається на складання MSBuild Tools 2014, які окремо скачати не вийде. Інсталяція зараз входить до складу дистрибутива Visual Studio 14 CTP.

Виявилося, що в склад .Net framework входить складання Microsoft.Build.dll v.4. В ній є реалізація читання файлу проекту C#. Можна самостійно реалізувати стикування цієї збірки з логікою Roslyn і отримати ізоляцію від MSBuild Tools. Реалізація буде приблизно такий:

var project = new Project(projectFilePath);
List<string> references = new List < string>();
List<SyntaxTree> trees = new List<SyntaxTree>();
foreach(var item in project.Items)
{
if (item.ItemType == "Reference")
{
references.Add(item.EvaluatedInclude);
}
else if (item.ItemType == "Compile")
{
string filePathToCompile = System.IO.Path.Combine(project.DirectoryPath, item.EvaluatedInclude);
var text = System.IO.File.ReadAllText(filePathToCompile);
trees.Add(CSharpSyntaxTree.ParseText(text));
}
}
var assemblyName = project.GetProperty("AssemblyName").EvaluatedValue;
var compilation = CSharpCompilation.Create(assemblyName)
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(new MetadataFileReference(typeof(Elisy.Configuration.Configuration).Assembly.Location))
.AddReferences(new MetadataFileReference(typeof(object).Assembly.Location))
.AddSyntaxTrees(trees.ToArray()); 


Якщо потрібно реалізувати більше властивостей проекту, то їх можна підглянути проект Roslyn

Отримання XML-файлів, сумісних з 1С: Підприємство

При генерації XML-файлів робота здебільшого ведеться з типами, їх членами і атрибутами. Виявилося, що об'єкти Roslyn CSharpCompilation дуже обмежені в коштах. Через них можна вийти на типи System.Type, можна отримати список атрибутів з урахуванням спадкування. Тому для повноцінного аналізу краще створити збірку засобами Roslyn і завантажити її для Reflection-аналізу:

MemoryStream ms = new MemoryStream();
compilation.Emit(ms);
var assembly = Assembly.Load(ms.ToArray());


Дивно, що не вдалося завантажити збірку як ReflectionOnlyLoad через помилку «missing api-ms-win-core-l1-2-0.dll». Помилка виникала при спробі прочитати входять до складу збірки типи. Тим не менш, збірка нормально завантажується і працює через звичайний виклик Load.

Генерація XML-файлів зводиться до перебору всіх типів, успадкованих від Configuration та основних об'єктів, до перебору властивостей об'єктів і атрибутів.

XML будується засобами XML LINQ, наприклад, так:

XElement configurationElement = new XElement(Compiler.MdClasses + "Language",
language.GetUuidXml(),
new XElement(Compiler.MdClasses + "Properties",
new XElement(Compiler.MdClasses + "Name", language.Name),
language.GetSynonimXml(),
language.GetCommentXml(),
new XElement(Compiler.MdClasses + "LanguageCode", language.GetAttribute().Value)
)
);
var document = CreateMetadataObjectDocument(configurationElement);
document.Save(Path.Combine(outFolder, String.Format("Language.{0}.xml", language.Name)));


Вдалося з'ясувати, що 1С лояльно ставиться до відсутності багатьох тегів, замінюючи їх значеннями за замовчуванням. Тому порожні значення в XML-файлах можна пропустити, тим самим скоротивши займаний розмір на диску. Розмір економії може бути вельми значною — до 40 відсотків. Використання ж cs-файлів замість XML дає ще більшу економію місця — до 60%.

Висновки

Засобами Visual Studio теоретично можливо створити альтернативний конфігуратор 1С. Для подальшого просування ідеї потрібно прийняти угоди щодо всіх основних об'єктів, зберігання форм, трансляції мови C# в 1С, директив препроцесора 1С, звичайних макетів і макетів СКД;

Поточна XML-вивантаження від 1С незручна для аналізу, тому що всі файли зібрані в єдиний каталог;

Поточна XML-вивантаження 1С надлишкова, і її розмір може бути зменшений без втрати даних до 40%, а зберігання вихідних кодів у вигляді C# дозволить економити до 60 відсотків дискового простору;

Читання csproj-проектів і sln-рішень Visual Studio вбудованими засобами Roslyn непросто реалізовується через складну доступності MSBuild Tools 2014. Компенсувати зараз можна класами .Net Framework 4 і власної ініціалізацією Roslyn.

Elisy.Configuration.zip (2,19 мб)

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.