Visual Studio + Roslyn = configurator for 1C: Enterprise

    Roslyn allows you to convert a C # project into an open XML format for 1C: Enterprise configurations. C # -project supported by Visual Studio is automatically provided with Intellisense, interactive syntax and type checking, refactoring, advanced project search, XmlDoc support. The customizable arrangement of project documents on disk and a more expressive and economical format makes the C # project on Visual Studio the best choice in versioning systems.

    It is clear that from a pure theory to the implementation of all the features of 1C is very far. The example in this article has the following limitations. The example implements support for several types of objects and several frequently encountered properties. Attributes of objects can be of the same type, although 1C allows a composite type. Code translation to 1C code is not supported. Implemented in English names.



    1C XML configuration upload



    Starting from version 8.3, 1C learned to upload / download configuration to / from open XML format. She does this in her original manner. Unloading, for example, UT 11.0.7 will take about 6 minutes. In this case, all configuration files will be dumped into a single directory. Files will be about 10K (ten thousand) in size approximately 430 MB. Such unloading is considered a huge achievement in the field of openness, because earlier the configuration was packed in a closed cf-format, which could only be recognized by specialized means: through .Net-compatible cfProject or v8unpack.

    This is one of the XML Language.Russian.xml files

    Русскийru


    Replacing XML Constructs with C # Definitions



    In a C # project, a class is responsible for each 1C object. If this is a directory, then the class inherits from the Catalog class, the enumeration inherits from the Enum class. Properties of objects and attributes are denoted by C # -attributes.

    As a result, the XML definition of the Russian language will take the form Russian.cs

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


    References to objects are defined through types of the type CatalogRef <>. For example, so

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


    The table part is defined as TabularSection <table part row>

    [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<РубрикаИзображения> Рубрики;
    


    Events of the object module and manager module are implemented as override methods. They may look like this:

    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(НайденнаяСтрока);
    }
    


    It should be noted that the methods are now not translated into the 1C code. It remains unclear what to do with the directives of the 1C preprocessor and whether multiple inheritance of the main 1C objects is necessary. G-stands for global context, since function calls in C # can only be done in conjunction with a class.

    Compiling the CSPROJ Project



    A previous article habrahabr.ru/post/245453 described the ability to compile a single CS file. More often you need to compile the whole project - several files.

    Roslyn has built-in classes for handling sln solutions and Visual Studio csproj projects. The problem is that they reference the individual assemblies shipped with MSBuild Tools. The latest version of Roslyn refers to the builds of MSBuild Tools 2014, which cannot be downloaded separately. Installation is now part of the Visual Studio 14 CTP distribution.

    It turned out that the .Net framework includes the assembly Microsoft.Build.dll v.4. It has an implementation for reading a C # project file. You can independently dock this assembly with Roslyn logic and get isolation from MSBuild Tools. The implementation will be something like this:

    var project = new Project(projectFilePath);
    List references = new List();
    List trees = new List();
    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());        
    


    If you need to implement more project properties, you can see them in the Roslyn project.

    Getting XML files compatible with 1C: Enterprise



    When generating XML files, most of the work is done with types, their members, and attributes. It turned out that the objects of Roslyn CSharpCompilation are very limited in funds. Through them, you cannot go to the types System.Type, you cannot get a list of attributes with inheritance in mind. Therefore, for a complete analysis, it is better to create an assembly using Roslyn tools and load it for Reflection analysis:

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


    It is strange that the assembly as ReflectionOnlyLoad could not be loaded due to the "missing api-ms-win-core-l1-2-0.dll" error. An error occurred while trying to read the types included in the assembly. However, the assembly loads normally and works through a regular Load call.

    Generating XML files boils down to enumerating all types inherited from Configuration and main objects, to enumerating properties of objects and attributes.

    XML is built using XML LINQ, for example, like this:

    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)));
    


    I managed to find out that 1C is loyal to the absence of many tags, replacing them with default values. Therefore, you can skip empty values ​​in XML files, thereby reducing the occupied size on the disk. The amount of savings can be very significant - up to 40 percent. Using cs-files instead of XML gives even more space saving - up to 60%.

    conclusions



    By means of Visual Studio it is theoretically possible to create an alternative 1C configurator. For further promotion of the idea, it is necessary to accept agreements regarding all the main objects, storage of forms, translation of the C # language in 1C, directives of the 1C preprocessor, ordinary layouts and layouts of access control systems;

    The current XML upload from 1C is inconvenient for analysis, because all files are collected in a single directory;

    The current 1C XML upload is redundant, and its size can be reduced without data loss by up to 40%, and storing source codes in the form of C # will save up to 60 percent of disk space;

    Reading csproj projects and sln solutions from Visual Studio with Roslyn built-in tools is not easy because of the difficult availability of MSBuild Tools 2014. You can now compensate with .Net Framework 4 classes and Roslyn native initialization.

    Elisy.Configuration.zip (2,19 mb)

    Also popular now: