.Net Core, 1C, динамическая компиляция, Scripting API

27.09.21

Разработка - Механизмы платформы 1С

Очень часто приходится использовать динамический код в 1С, используя Выполнить или Вычислить. Аналогичная задача и для использования скриптов на .Net. Я постарался показать, как можно скрестить ежа с ужом и получить удобный код. В этой статье много вражеского кода для чистого одноэсника, но все когда-то бывает впервые.

Это продолжение статей Кроссплатформенное использование классов .Net в 1С через Native ВК. Или замена COM на Linux
Кроссплатформенное использование классов .Net в 1С через Native ВК. Или замена COM на Linux II
Асинхронное программирование в 1С через использование классов .Net из Native ВК
1С, Linux, Excel, Word, OpenXML, ADO, Net Core

На данный момент в .Net Core 2 варианта динамической компиляции 1. Это аналог CodeDom Microsoft.CodeAnalysis.CSharp.CSharpCompilation 
2. Roslyn Scripting Api. Примеры здесь

    var compilation = Microsoft.CodeAnalysis.CSharp.CSharpCompilation.Create("a")
    .WithOptions(new Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions(Microsoft.CodeAnalysis    .OutputKind.DynamicallyLinkedLibrary))
    .AddReferences(
     Microsoft.CodeAnalysis.MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location))
    .AddSyntaxTrees(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText(
        @"
          using System;

          public  class C
          {
  
            public  C(){}
            public string  M()
              {
               return ""Hello Roslyn."";
             }
           }"));

      var fileName = @"d:\NetStandart\TestCoreNetApp\src\TestCoreNetApp\bin\Debug\netcoreapp1.0\a.dll";

      compilation.Emit(fileName);

      var a = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromAssemblyPath(fileName);


      Type тип = a.GetType("C");
      var obj = Activator.CreateInstance(тип);

      var res = тип.GetMethod("M").Invoke(obj, null);
      Console.WriteLine(res.ToString());


 Этот подход хорош, когда нужно обновить библиотеку. Но при этом создается DLL со всеми вытекающими.

 Второй способ мне нравится больше. Возьмем на примере получения делегата.

            string words = "надо заменить все первые буквы в словах на заглавные";
            string pattern = @"\w+";
                        
            var scr = Microsoft.CodeAnalysis.Scripting.ScriptOptions.Default;
            var mscorlib = Assembly.Load(System.Runtime.Loader.AssemblyLoadContext.GetAssemblyName(@"c:\Users\Smirnov_SA\.nuget\packages\Microsoft.NETCore.Portable.Compatibility\1.0.1\ref\netcore50\mscorlib.dll"));
            scr =scr.WithReferences(mscorlib, typeof(MulticastDelegate).GetTypeInfo().Assembly, typeof(System.Runtime.CompilerServices.IStrongBox).GetTypeInfo().Assembly, typeof(MatchEvaluator).GetTypeInfo().Assembly, typeof(Regex).GetTypeInfo().Assembly)
            .WithImports("System", "System.Text.RegularExpressions");


            string КодДелегата = @"return (MatchEvaluator)((match) =>
            {
              string x = match.Value;
            // If the first char is lower case...
                if (char.IsLower(x[0]))
                {
                  // Capitalize it.
                    return char.ToUpper(x[0]) + x.Substring(1, x.Length - 1);
                }
                  return x;

            });";
            var result = Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.EvaluateAsync(КодДелегата, scr).Result;
                  MatchEvaluator evaluator = (MatchEvaluator)result;
            
            Console.WriteLine(Regex.Replace(words, pattern, evaluator));


 Обязательно нужно указать ссылки на:
   mscorlib.dll
   System.Private.CoreLib.ni.dll
   System.Runtime.dll 


 Для компиляции используются следующие библиотеки:
   "Microsoft.CodeAnalysis.CSharp": "2.0.0-beta3",
    "Microsoft.CodeAnalysis.CSharp.Scripting": "2.0.0-beta3",
    "Microsoft.CodeAnalysis.Scripting.Common": "2.0.0-beta3",
     Microsoft.CodeAnalysis.Scripting


   По динамической компиляции. В свое время работал с запчастями для автомобилей. А там куча поставщиков и клиентов. При этом прайсы на миллионы позиций. И каждый горазд давать данные в своем формате.  Было проще для каждого клиента создавать код,  записывать в справочник и использовать через Выполнить или Вычислить. Правда, при работе с миллионными прайсами этот подход тормозил.
 
Так или иначе приходилось писать DLL и работать через COM.

  C помощью динамической компиляции можно хранить текст кода и применять его в зависимости от условий. В том числе динамически формировать по условиям, а скомпилированный делегат можно кэшировать для повторного использования.


 Скорость компиляции достаточно высокая (кроме первого раза секунд 5).


 Перейдем к 1С. Так воторй алгоритм на 1С выглядит так

            стр  = "return (MatchEvaluator)((match) =>
            |{
            |  string x = match.Value;
            |// If the first char is lower case...
            |if (char.IsLower(x[0]))
            |{
            |// Capitalize it.
            |return char.ToUpper(x[0]) + x.Substring(1, x.Length - 1);
            |}
            |return x;
            |
            |});";
            
            текст = "надо заменить все первые буквы в словах на заглавные";
            Образец = "\w+";
            // MatchEvaluator evaluator = (MatchEvaluator)ПолучитьДелегат();
            ScriptOptions=ъТип("Microsoft.CodeAnalysis.Scripting.ScriptOptions","Microsoft.CodeAnalysis.Scripting");
            CSharpScript=ъТип("Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript","Microsoft.CodeAnalysis.CSharp.Scripting");

            scr = ъ(ScriptOptions.Default);
            mscorlibСборка = ъ(Врап.Сборка("mscorlib.dll",Истина));
            Private_CoreLibСборка=ъ(Врап.Сборка("System.Private.CoreLib.ni",Истина));
            System_RuntimeСборка=ъ(Врап.Сборка("System.Runtime",Истина));
            RegularExpressionsСборка=ъ(Врап.Сборка("System.Text.RegularExpressions",Истина));
            Regex=ъ(RegularExpressionsСборка.GetType("System.Text.RegularExpressions.Regex"));
            scr =ъ(scr.WithReferences(mscorlibСборка.ПолучитьСсылку(), Private_CoreLibСборка.ПолучитьСсылку(), System_RuntimeСборка.ПолучитьСсылку(), RegularExpressionsСборка.ПолучитьСсылку()));
            
            scr =ъ(scr.WithImports("System", "System.Text.RegularExpressions"));

            evaluator = ъ(ъ(CSharpScript.EvaluateAsync(стр, scr.ПолучитьСсылку())).Result);
            Сообщить(Regex.Replace(текст, Образец, evaluator.ПолучитьСсылку()));


В общем, ничего особенного. Получили сборки, дали ссылки на них, скомпилировали, вызвали.

Перейдем к более сложным примерам. Так, в предыдущей статье я показывал примеры использования DocumentFormat.OpenXml на примере чтения Excel и Word.

Там была проблема в скорости из-за приведения строки к объекту через функцию, и сама скорость вызова из 1С в 5 раз медленнее её родного кода.

Поэтому вынесем большую часть кода в .Net. Аналогичный вариант есть на большом .Net  .Net в 1С. На примере использования HTTPClient,AngleSharp. Удобный парсинг сайтов с помощью библиотеки AngleSharp, в том числе с авторизацией аля JQuery с использованием CSS селекторов. Динамическая компиляция (пример в конце статьи).

Создадим класс и скопируем его в макет. Суть класса - прочитать данные ячеек и сгруппировать их по номеру строки

public class ДанныеЯчейки
    {

        public string ИмяЯчейки;
        public string ИмяКолонки;
        public int НомСтроки;
        public string ЗначениеЯчейки;

    }
    public class ExcelReader
    {
        static Regex ШаблонКолонки = new Regex("[A-Za-z]+");
        OpenXmlElementList ОбщиеСтроки;

        void ЗаписатьДанныеАдреса(string адрес, ДанныеЯчейки данныеЯчейки)
        {

            данныеЯчейки.ИмяЯчейки = адрес;
            var match = ШаблонКолонки.Match(адрес);
            var ИмяКолонки = match.Value;
            var НомерСтроки = int.Parse(адрес.Substring(ИмяКолонки.Length));
            данныеЯчейки.ИмяКолонки = ИмяКолонки;
            данныеЯчейки.НомСтроки = НомерСтроки;

        }
        void ДобавитьДанныеОбЯчейке(List<ДанныеЯчейки> ДанныеЭселя, Cell cell)
        {
            var адрес = cell.CellReference.InnerText;
            var text = cell.CellValue?.Text;
            var DataType = cell.DataType;
            string res = text;
            if (DataType != null && DataType == CellValues.SharedString)
            {
                int ssid = int.Parse(text);
                res = ОбщиеСтроки[ssid].InnerText;
            }

            if (res == null) return;

            var result = new ДанныеЯчейки();
            ЗаписатьДанныеАдреса(адрес, result);
            result.ЗначениеЯчейки = res;

            ДанныеЭселя.Add(result);
        }
        public List<ДанныеЯчейки> ReadExcel(string fileName)
        {
            List<ДанныеЯчейки> ДанныеЭселя = new List<ДанныеЯчейки>();
            using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                using (SpreadsheetDocument doc = SpreadsheetDocument.Open(fs, false))
                {
                    var workbookPart = doc.WorkbookPart;

                    // Строки хранятся отдельно
                    // В ячейках хранится индекс
                    var pt = workbookPart.GetPartsOfType<SharedStringTablePart>();
                    var sstpart = pt.First();
                    var sst = sstpart.SharedStringTable;
                    ОбщиеСтроки = sst.ChildElements;

                    var workbook = workbookPart.Workbook;

                    // Получим список страниц
                    var sheet = workbook.Descendants<Sheet>().First();

                    var worksheetPart = (DocumentFormat.OpenXml.Packaging.WorksheetPart)workbookPart.GetPartById(sheet.Id);

                    var worksheet = worksheetPart.Worksheet;

                    var cells = worksheet.Descendants<Cell>();


                    // One way: go through each cell in the sheet
                    foreach (Cell cell in cells)
                    {
                        ДобавитьДанныеОбЯчейке(ДанныеЭселя, cell);
                    }
                }
            }

            return ДанныеЭселя;
        }

        static string НайтиИмяПоследнейКолонки(List<ДанныеЯчейки> Тз)
        {
            var рез = "";
            var ДлинаРез = 0;
            foreach (var стрТз in Тз)
            {
                var Стр = стрТз.ИмяКолонки;
                var СтрДл = Стр.Length;

                if (СтрДл > ДлинаРез)
                {
                    ДлинаРез = СтрДл;
                    рез = Стр;
                }
                else if (СтрДл == ДлинаРез && string.Compare(Стр, рез, true) > 0)
                    рез = Стр;

            }
            return рез;
        }

        public static object ПолучитьДанныеЭкселя(string fileName)
        {

            var res = new ExcelReader();
            var Данные = res.ReadExcel(fileName);
            var данныеЭкселя = Данные.GroupBy(ё => ё.НомСтроки).Select(ъ => new { НомСтроки = ъ.Key, Ячейки = ъ.ToArray() }).OrderBy(ь => ь.НомСтроки);
            var ИмяПоследнейКолонки = НайтиИмяПоследнейКолонки(Данные);
            return new { ДанныеЁкселя = данныеЭкселя.ToList(), ИмяПоследнейКолонки = ИмяПоследнейКолонки };

        }

     }

             return new Func<string, object>(ExcelReader.ПолучитьДанныеЭкселя);

 

Мы описали класс чтения и возвращаем ссылку на делегат, принимающий путь к файлу и возвращающий анонимный класс. Все равно в 1С мы будем работать с ним через рефлексию. Обратите внимание, что здесь описаны 2 класса.

Теперь вызовем этот код из 1С.

 

        ScriptOptions=ъТип("Microsoft.CodeAnalysis.Scripting.ScriptOptions","Microsoft.CodeAnalysis.Scripting");
	CSharpScript=ъТип("Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript","Microsoft.CodeAnalysis.CSharp.Scripting");
	
	scr = ъ(ScriptOptions.Default);
	mscorlibСборка = ъ(Врап.Сборка("mscorlib.dll",Истина));
	Private_CoreLibСборка=ъ(Врап.Сборка("System.Private.CoreLib.ni",Истина));
	System_RuntimeСборка=ъ(Врап.Сборка("System.Runtime",Истина));
	RegularExpressionsСборка=ъ(Врап.Сборка("System.Text.RegularExpressions",Истина));
	OpenXmlСбока=ъ(Врап.Сборка("DocumentFormat.OpenXml.dll"));
	LinqСбока=ъ(Врап.Сборка("System.Linq", истина));
	FileSystemСбока=ъ(Врап.Сборка("System.IO.FileSystem", истина));
	
	Regex=ъ(RegularExpressionsСборка.GetType("System.Text.RegularExpressions.Regex"));
	scr =ъ(scr.WithReferences(mscorlibСборка.ПолучитьСсылку(), Private_CoreLibСборка.ПолучитьСсылку(), System_RuntimeСборка.ПолучитьСсылку(), RegularExpressionsСборка.ПолучитьСсылку(),OpenXmlСбока.ПолучитьСсылку(),LinqСбока.ПолучитьСсылку(),FileSystemСбока.ПолучитьСсылку()));
	scr =ъ(scr.WithImports("System", "System.Collections.Generic", "System.Linq", "System.IO", "DocumentFormat.OpenXml", "DocumentFormat.OpenXml.Packaging", "DocumentFormat.OpenXml.Spreadsheet", "System.Text.RegularExpressions"));
	
	Текст=ПолучитьМакет("ТекстКлассаЧтенияЕксель").ПолучитьТекст();
	
	Делегат = ъ(ъ(CSharpScript.EvaluateAsync(Текст, scr.ПолучитьСсылку())).Result);
	Данные= ъ(Делегат.DynamicInvoke(ИмяФайла));
	
	Сообщить(Данные.ИмяПоследнейКолонки);
	
	рез=новый ТаблицаЗначений;
	ПоследняяКолонка=Данные.ИмяПоследнейКолонки;
	СоздатьКолонки(рез.Колонки,ПоследняяКолонка);
	Колонки=рез.Колонки;
	
	Тз=ъ(Данные.ДанныеЁкселя);
	Тз=ъ(Врап.ПолучитьЭнумератор(Тз.ПолучитьСсылку()));
	
	сч=1;
	// Получили сгруппированные данные по строкам
	Пока Тз.MoveNext() Цикл
		стрТз= ъ(Тз.Current);
		
		НомСтроки=стрТз.НомСтроки;
		Пока сч<НомСтроки Цикл
			сч=сч+1;
			рез.Добавить();
		КонецЦикла;
		сч=сч+1;
		стр=рез.Добавить();
		
                // получим ячейки по строке
		ТзГр=ъ(стрТз.Ячейки);
		ТзГр=ъ(Врап.ПолучитьЭнумератор(ТзГр.ПолучитьСсылку()));
		
		Пока ТзГр.MoveNext() Цикл
			стрТзГр= ъ(ТзГр.Current);
			ИмяКолонки=стрТзГр.ИмяКолонки;
			ЗначениеЯчейки=стрТзГр.ЗначениеЯчейки;
			// Можно конечно получить индекс зная смещение символа 64 относительно 1 и 26 разрядную систему
			// но найдем колонку по имени и её индекс
			Колонка=Колонки.Найти(ИмяКолонки);
			стр.Установить(Колонки.Индекс(Колонка),ЗначениеЯчейки); 
		КонецЦикла;	
	КонецЦикла;

Теперь скорость обработки Экселя значительно увеличилась, а затраты на компиляцию соизмеримы с затратами на чтения файлов.

Посмотрим процесс чтения Word. Не мудрствуя лукаво, я взял готовый класс здесь. Тем более там все на английском.

 

// Исходники можно скачать здесь
    //https://code.msdn.microsoft.com/office/CSOpenXmlGetPlainText-554918c3/sourcecode?fileId=71592&pathId=851860130
    public class GetWordPlainText : IDisposable
    {
        // Specify whether the instance is disposed. 
        private bool disposed = false;

        // The word package 
        private WordprocessingDocument package = null;

        /// <summary> 
        ///  Get the file name 
        /// </summary> 
        private string FileName = string.Empty;

        /// <summary> 
        ///  Initialize the WordPlainTextManager instance 
        /// </summary> 
        /// <param name="filepath"></param> 
        public GetWordPlainText(string filepath)
        {
            this.FileName = filepath;
            if (string.IsNullOrEmpty(filepath) || !File.Exists(filepath))
            {
                throw new Exception("The file is invalid. Please select an existing file again");
            }

            this.package = WordprocessingDocument.Open(filepath, true);
        }

        /// <summary> 
        ///  Read Word Document 
        /// </summary> 
        /// <returns>Plain Text in document </returns> 
        public string ReadWordDocument()
        {
            StringBuilder sb = new StringBuilder();
            OpenXmlElement element = package.MainDocumentPart.Document.Body;
            if (element == null)
            {
                return string.Empty;
            }

            sb.Append(GetPlainText(element));
            return sb.ToString();
        }

        /// <summary> 
        ///  Read Plain Text in all XmlElements of word document 
        /// </summary> 
        /// <param name="element">XmlElement in document</param> 
        /// <returns>Plain Text in XmlElement</returns> 
        public string GetPlainText(OpenXmlElement element)
        {
            StringBuilder PlainTextInWord = new StringBuilder();
            foreach (OpenXmlElement section in element.Elements())
            {
                switch (section.LocalName)
                {
                    // Text 
                    case "t":
                        PlainTextInWord.Append(section.InnerText);
                        break;

                    case "cr":                          // Carriage return 
                    case "br":                          // Page break 
                        PlainTextInWord.Append(Environment.NewLine);
                        break;

                    // Tab 
                    case "tab":
                        PlainTextInWord.Append("\t");
                        break;

                    // Paragraph 
                    case "p":
                        PlainTextInWord.Append(GetPlainText(section));
                        PlainTextInWord.AppendLine(Environment.NewLine);
                        break;

                    default:
                        PlainTextInWord.Append(GetPlainText(section));
                        break;
                }
            }

            return PlainTextInWord.ToString();
        }

        #region IDisposable interface 

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            // Protect from being called multiple times. 
            if (disposed)
            {
                return;
            }

            if (disposing)
            {
                // Clean up all managed resources. 
                if (this.package != null)
                {
                    this.package.Dispose();
                }
            }

            disposed = true;
        }
        #endregion

        public static string GetText(string FileName)
        {

            using (var pt = new GetWordPlainText(FileName))
            {

                return pt.ReadWordDocument();
            }
        }
    }
    
    return new Func<string,string>(GetWordPlainText.GetText);

 

Но вернемся к Руслишу:

        ScriptOptions=ъТип("Microsoft.CodeAnalysis.Scripting.ScriptOptions","Microsoft.CodeAnalysis.Scripting");
	CSharpScript=ъТип("Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript","Microsoft.CodeAnalysis.CSharp.Scripting");
	
	scr = ъ(ScriptOptions.Default);
	mscorlibСборка = ъ(Врап.Сборка("mscorlib.dll",Истина));
	Private_CoreLibСборка=ъ(Врап.Сборка("System.Private.CoreLib.ni",Истина));
	System_RuntimeСборка=ъ(Врап.Сборка("System.Runtime",Истина));
	RegularExpressionsСборка=ъ(Врап.Сборка("System.Text.RegularExpressions",Истина));
	OpenXmlСбока=ъ(Врап.Сборка("DocumentFormat.OpenXml.dll"));
	LinqСбока=ъ(Врап.Сборка("System.Linq", истина));
	FileSystemСбока=ъ(Врап.Сборка("System.IO.FileSystem", истина));
	
	Regex=ъ(RegularExpressionsСборка.GetType("System.Text.RegularExpressions.Regex"));
	scr =ъ(scr.WithReferences(mscorlibСборка.ПолучитьСсылку(), Private_CoreLibСборка.ПолучитьСсылку(), System_RuntimeСборка.ПолучитьСсылку(),OpenXmlСбока.ПолучитьСсылку(),FileSystemСбока.ПолучитьСсылку()));
	scr =ъ(scr.WithImports("System", "System.Text",  "System.IO", "DocumentFormat.OpenXml", "DocumentFormat.OpenXml.Packaging"));
	
	Текст=ПолучитьМакет("ТекстЧтенияВорд").ПолучитьТекст();
	
	Делегат = ъ(ъ(CSharpScript.EvaluateAsync(Текст, scr.ПолучитьСсылку())).Result);
	стр = Делегат.DynamicInvoke(ИмяФайла);
	
	Текст=Новый ТекстовыйДокумент;
	Текст.ДобавитьСтроку(стр);
	Текст.Показать();

 

Основная задача указать ссылки на используемые сборки и пространство имен.

В следующей статье я сделаю динамическое создание обертки над объектами, использующими события, по аналогии с
.NET(C#) для 1С. Динамическая компиляция класса обертки для использования .Net событий в 1С через ДобавитьОбработчик или ОбработкаВнешнегоСобытия

.Net Core Scripting API <blockquote>

См. также

Поинтегрируем: сервисы интеграции – новый стандарт или просто коннектор?

Обмен между базами 1C Администрирование СУБД Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

В платформе 8.3.17 появился замечательный механизм «Сервисы интеграции». Многие считают, что это просто коннектор 1С:Шины. Так ли это?

11.03.2024    4456    dsdred    53    

70

Как готовить и есть массивы

Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Все мы используем массивы в своем коде. Это один из первых объектов, который дают ученикам при прохождении обучения программированию. Но умеем ли мы ими пользоваться? В этой статье я хочу показать все методы массива, а также некоторые фишки в работе с массивами.

24.01.2024    5277    YA_418728146    25    

63

Планы обмена VS История данных

Обмен между базами 1C Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Вы все еще регистрируете изменения только на Планах обмена и Регистрах сведений?

11.12.2023    6396    dsdred    36    

111

1С-ная магия

Механизмы платформы 1С Бесплатно (free)

Язык программирования 1С содержит много нюансов и особенностей, которые могут приводить к неожиданным для разработчика результатам. Сталкиваясь с ними, программист начинает лучше понимать логику платформы, а значит, быстрее выявлять ошибки и видеть потенциальные узкие места своего кода там, где позже можно было бы ещё долго медитировать с отладчиком в поисках источника проблемы. Мы рассмотрим разные примеры поведения кода 1С. Разберём результаты выполнения и ответим на вопросы «Почему?», «Как же так?» и «Зачем нам это знать?». 

06.10.2023    18464    SeiOkami    46    

118

Дефрагментация и реиндексация после перехода на платформу 8.3.22

Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Начиная с версии платформы 8.3.22 1С снимает стандартные блокировки БД на уровне страниц. Делаем рабочий скрипт, как раньше.

14.09.2023    12075    human_new    27    

74

Валидация JSON через XDTO (включая массивы)

WEB-интеграция Универсальные функции Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    8801    YA_418728146    6    

141

Внешние компоненты Native API на языке Rust - Просто!

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Внешние компоненты для 1С можно разработывать очень просто, пользуясь всеми преимуществами языка Rust - от безопасности и кроссплатформенности до удобного менеджера библиотек.

20.08.2023    6273    sebekerga    54    

94

Все скопируем и вставим! (Буфер обмена в 1С 8.3.24)

Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Рассмотрим новую возможность 8.3.24 и как её можно эффективно использовать

27.06.2023    15968    SeiOkami    31    

103
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. cool.vlad4 2 07.09.16 15:52 Сейчас в теме
+1. все это прикольно. но ума не приложу, как в реале писать в итоге код таким образом . выходной код , напоминает код с которым я как-то сталкивался , - powershell , который динамически компилит огромную простыню C# кода , аналогично, производит всякие действия, и в итоге получается результат. попытки что-либо изменить , приводили к ошибкам. оттестировать, тоже было непонятно как. в итоге , я переписал все на C# . но вот в случае с 1С, как быть . на самом деле использовать dotnet , идея заманчивая , появляется в среде 1С периодически на протяжении долгого периода времени. но вот как сделать хорошую интеграцию на уровне кода, пока непонятно. я к примеру, недавно написал COM компоненту, на C# , которая по заданному шаблону docx , формирует документ. мне это проще. нежели писать такой код:

scr = ъ(ScriptOptions.Default);
mscorlibСборка = ъ(Врап.Сборка("mscorlib.dll",Истина));
Private_CoreLibСборка=ъ(Врап.Сборка("System.Private.CoreLib.ni",Истина));
System_RuntimeСборка=ъ(Врап.Сборка("System.Runtime",Истина));
RegularExpressionsСборка=ъ(Врап.Сборка("System.Text.RegularExpressions",Истина));
OpenXmlСбока=ъ(Врап.Сборка("DocumentFormat.OpenXml.dll"));
LinqСбока=ъ(Врап.Сборка("System.Linq", истина));
FileSystemСбока=ъ(Врап.Сборка("System.IO.FileSystem", истина));
и т.д.

есть мысли как бороться со сложностью читаемости и сопровождения результируещего кода?
утюгчеловек; +1 Ответить
2. Serginio 938 07.09.16 16:01 Сейчас в теме
Ну проблема для начало в том, что в Native API нельзя возвращать объекты. Поэтому приходится возвращать строку и из неё создавать ВК.

//1С при передаче по ссылке свойства ВК Список.Current
// при выходе из метода присваивает  Список.Current значение переданное изначально
// Поэтому помечаем входной параметр как Знач
//Или же делать так, если методы изменить нельзя 
// То нужно присвоить значение переменной и вызвать метод передав в параметрах эту переменную
//Стр=Список.Current; 
//Зазача=ъ(Стр);
Функция Ъ(знач Ссылка)
	
	// Создаем объект по ссылке полученной из методов .Net классов
	//Физически это строка ёЁ<Ьъ>№_%)Э?&2 содержащее 12 символов для отделения их от других строк
	//и индекс в спике исполуемых объектов на стороне .Net
	
	рез = Новый("AddIn.NetObjectToNative.NetObjectToNative");
	// И установим ссылку
	рез.УстановитьСсылку(Ссылка);    
	возврат  рез
КонецФункции // СоздатьОбъектПоСсылке()
Показать


Кстати на данные момент я не могу написать DLL для Core .Net для использования OpenXML. Нужно, что бы компонент был в NuGet.
Кроме того я предполагаю использовать динамическую компиляцию для обертки событий.
3. cool.vlad4 2 07.09.16 16:08 Сейчас в теме
(2) это да. вот и получается , что придется либо переизобретать тот же COM, либо делать подобные workaround. но просто выходной код получается, уж очень непростым для сопровождения. примеры-то простые. в более сложных случаях будет еще более увесистый код.
4. Serginio 938 07.09.16 16:14 Сейчас в теме
(3) Я это прекрасно понимаю. Вообще эти разработки для того, что бы подвинуть 1С к изменению своей политики. На самом деле нет особых причин не использовать объекты в параметрах или при возврате объекта из метода.

Я уже писал в этой статье Кроссплатформенное использование классов .Net в 1С через Native ВК. Или замена COM на Linux

1. Абсолютно не нужны методы FindMethod, FindProp, IsPropReadable, IsPropWritable, GetNParams, HasRetVal, GetParamDefValue

Так как у методов

bool CallAsProc

bool CallAsFunc

bool SetPropVal и bool GetPropVal есть возвращаемое значение об успешном выполнении

Информация об ошибке возвращается через AddError.

Да и вызов по индексу это анахронизм от IDiapatch, где было описание диспинтерфейсов для увеличения скорости вызова.

2. При возвращении методами SetPropVal и GetPropVal исключение не вызывается

3. Зачем-то происходит установка свойств там, где в коде этого не требуется.

4. Вызывается метод как функция там, где метод вызывается как процедура.

5. Один из основных - это нельзя вернуть и передать экземпляр ВК из методов ВК.

Я лично не вижу никаких проблем. Определить значение для такого типа
Показать
duhin; cool.vlad4; +2 Ответить
6. Serginio 938 07.09.16 16:57 Сейчас в теме
(3) Код то будет аналогичным. Нужно просто добавить ссылки на используемые DLL и пространства имен.
ссылки на DLL можно передавать в виде путей
5. Serginio 938 07.09.16 16:15 Сейчас в теме
Кроме того

Например практически все программисты 1С используют ComОбъект.

По моей методе можно использовать
NetОбъект,NetТип
JavaОбъект,JavaТип

И эти объявления будут реально кроссплатформенны.
При это различия с ComОбъект минимальны. Имя класса равноценно комовскому ProgID. При этом нет ограничений на используемые типы.
Ты можешь написать свою библиотеку поместить в определенное место и использовать её вместо COM. Без регистрации итд. Расширять возможности 1С станет легче.

Например сейчас в конфигураторе куча функций, которые есть в стандартных библиотеках. При этом функционал 1С функций сильно недотягивает до стандартных библиотек.
Учитывая кроссплатформенность можно использовать нетовские или явовские библиотеки везде где можно. Упрощая программирование.
А что касается работы с HTTP,SMPT, JSON то эти библиотеки были задолго до того как 1С их реализовывала, при этом с ошибками и функционалом сильно недотягивающих до .Net или Java.
Стоит ли тратить время на то, что можно взять из стандартных библиотек. А ведь можно легко расширить за счет своих сборок.

При этом например нетовская библиотека весит всего 65 мегабайт, которую можно включить в дистрибутив и главное это кроссплатформенное решение. Причем нет ничего сложного передавать в параметрах и объекты 1С которые поддерживают аналогичный интерфейс. Можно пойти по пути подсчета ссылок в 1С, а доступ к объектам 1С только на время вызова метода ВК.

Например Использование сборок .NET в 1С 7.x b 8.x. Создание внешних Компонент многие бы использовали на равне с ComОбъект, но главная причина её неприменения в том, что она неинтегрирована в 1С.

Даже если 1С не хочет интегрировать .Net в 1С, то можно сделать вариант ВК под .Net и Java . Прежде всего для получения объектов ВК и передачи их в параметрах. Кроме того можно добавить доступ по индексу [], получения итератора.
Оставьте свое сообщение