В этой статье покажу, как можно асинхронно вызывать асинхронные методы и вызывать ВнешнееСобытие в 1С из сборки .Net
Это продолжение статьи Кроссплатформенное использование классов .Net в 1С через Native ВК. Или замена COM на Linux II
В .Net сейчас во многих классах есть асинхронные методы. В 1С их можно применять например так
Стр=ъ(Клиент.GetStringAsync(uriSources)).Result;
И напомню про синонимы из предыдущей статьи/ Так добавив синоним
Врап.ДобавитьСиноним(HTTPClient.ПолучитьСсылку(),"ПолучитьСтроку","GetStringAsync");
Врап.ДобавитьСиноним(Task.ПолучитьСсылку(),"Результат","Result");
Я могу использовать все на русском
Стр=ъ(Клиент.ПолучитьСтроку(адрес)).Результат;
Но можно использовать асинхронные методы так
Клиент = ъ(Врап.Новый(HttpClient.ПолучитьСсылку(),handler.ПолучитьСсылку()));
лист=ъНовый("System.Collections.Generic.List`1[System.Threading.Tasks.Task]");
Для каждого стр из ПолучитьСписокURL() Цикл
Задача=ъ(Клиент.GetStringAsync(стр));
лист.Add(задача.ПолучитьСсылку());
КонецЦикла;
Task=ъТип("System.Threading.Tasks.Task");
Пока лист.Count>0 Цикл
массив=ъ(лист.ToArray());
индекс = Task.WaitAny(массив.ПолучитьСсылку());
Сообщить(индекс);
результат = ъ(лист.get_Item(индекс)).Result;
Сообщить(СтрДлина(результат));
лист.RemoveAt(индекс);
КонецЦикла;
Это конечно не совсем асинхронное программирование, но все же лучше чем отдельно для каждого запроса вызывать синхронный метод.
Но нужно делать асинхронный вызов. Для этого создал простенький класс
public class АсинхронныйВыполнитель
{// Данные связанные с задачей, что бы понять, что к чему при получении события
public object ДанныеДляЗадача;
// Сама задача
public Task Задача;
// Имя метода 1С обрабатывающего окончание задачи.
public String ИмяМетода;
public АсинхронныйВыполнитель(Task Задача, String ИмяМетода, Object ДанныеДляЗадача)
{
this.Задача = Задача;
this.ДанныеДляЗадача = ДанныеДляЗадача;
this.ИмяМетода = ИмяМетода;
Задача.ContinueWith((t) =>
{
var AW = new AutoWrap(this);
AutoWrap.ВызватьВнешнееСобытие1С("АсинхронныйВыполнитель", ИмяМетода, AW.ПолучитьСсылку());
});
}
}
А на стороне 1С
Функция ПолучитьДанныеДляЗадачи(адрес,Номер)
объект=ъНовый(ExpandoObject.ПолучитьСсылку());
объект.Адрес=адрес;
объект.Номер=Номер;
возврат объект;
КонецФункции // ПолучитьДанныеДляЗадачи()
Процедура ТестАсинхронногоВыполнителяНажатие(Элемент)
Клиент = ъ(Врап.Новый(HttpClient.ПолучитьСсылку(),handler.ПолучитьСсылку()));
лист=ъНовый("System.Collections.Generic.List`1[System.Threading.Tasks.Task]");
сч=0;
Для каждого стр из ПолучитьСписокURL() Цикл
// Получаем задачу
Задача=ъ(Клиент.GetStringAsync(стр));
// Получаем данные привязанные к данной задаче
объект=ПолучитьДанныеДляЗадачи(стр,сч);
//public static void ВыполнитьЗадачу(System.Threading.Tasks.Task Задача, String ИмяМетода, Object ДанныеДляЗадача)
Врап.ВыполнитьЗадачу(Задача.ПолучитьСсылку(),"ПолученаСтраница",объект.ПолучитьСсылку());
сч=сч+1;
КонецЦикла;
// Добавим тест на ошибку
Тестовый=ъТип("TestDllForCoreClr.Тестовый","TestDllForCoreClr");
Тест=ъ(Врап.Новый(Тестовый.ПолучитьСсылку()," Свойство из Конструктора"));
Thread=ъТип("System.Threading.Thread");
// При передаче 2 вызывается ошибка
Задача=ъ(Тест.ЗадачаСОшибкойAsync(2));
объект=ПолучитьДанныеДляЗадачи("ЗадачаСОшибкойAsync",2);
Врап.ВыполнитьЗадачу(Задача.ПолучитьСсылку(),"ПолученаСтраница",объект.ПолучитьСсылку());
Задача2=ъ(Тест.ЗадачаСОшибкойAsync(0));
объект=ПолучитьДанныеДляЗадачи("ЗадачаСОшибкойAsync",0);
Врап.ВыполнитьЗадачу(Задача2.ПолучитьСсылку(),"ПолученаСтраница",объект.ПолучитьСсылку());
КонецПроцедуры
Процедура ПолученаСтраница(знач данные)
Задача=ъ(данные.Задача);
ДанныеДляЗадача=ъ(данные.ДанныеДляЗадача);
Сообщить("Адрес="+ДанныеДляЗадача.Адрес);
Сообщить("Номер="+ДанныеДляЗадача.Номер);
// При возникновении ошибки при выполнении задачи
// IsFaulted будет истина, а в Exception будет ошибка
Если (Задача.IsFaulted) Тогда // Ошибка выполнения
ошибка=Задача.Exception;
Сообщить("Ошибка "+Врап.ВСтроку(ошибка));
// Если сделать как ниже 1С хочет присвоить Задача.Exception новое значение
// Даже если оно не было изменено
// Так как считает, что параметр передан по ссылке
// Сообщить("Ошибка "+Врап.ВСтроку(Задача.Exception));
возврат;
КонецЕсли;
результат=Задача.Result;
Сообщить(СтрДлина(результат));
КонецПроцедуры
Процедура ВнешнееСобытие(Источник, Событие, Данные)
// Вставить содержимое обработчика.
Сообщить("Источник="+Источник);
Сообщить("Событие="+Событие);
Сообщить("Данные="+Данные);
Если Источник="АсинхронныйВыполнитель" Тогда
Данные=ъ(Данные);
Выполнить(Событие+"(Данные)");
КонецЕсли;
КонецПроцедуры
Немного поясню по коду.
Так как в Native ВК нельзя использовать ДобавитьОбработчик то сделаем его эмуляцию передав в АсинхронныйВыполнитель имя метода, который нужно вызвать в 1С
Врап.ВыполнитьЗадачу(Задача.ПолучитьСсылку(),"ПолученаСтраница",объект.ПолучитьСсылку());
Который будет вызываться при срабатывании внешнего события
Если Источник="АсинхронныйВыполнитель" Тогда
Данные=ъ(Данные);
Выполнить(Событие+"(ПолученаСтраница)");
КонецЕсли;
В данном в переменной Событие хранится ПолученаСтраница и будет вызван метод
ПолученаСтраница(ПолученаСтраница)
Теперь про использование Внешнего события в .Net сборке
Для этого нужно определить поле
public Action<string, string, string> ВнешнееСобытие1С;
И установить его из 1С
Тестовый=ъТип("TestDllForCoreClr.Тестовый, TestDllForCoreClr, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
Тест=ъ(Врап.Новый(Тестовый.ПолучитьСсылку()," Свойство из Конструктора"));
Делегат=Ъ(Врап.ПолучитьДелегатВнешнегоСобытия1C());
Тест.ВнешнееСобытие1С=Делегат.ПолучитьСсылку();
Тест.TestВнешнегоСобытия();
Внутри .Net класса вызвать метод
this?.ВнешнееСобытие1С("Тестовый", "ТестовоеСообщение", значение);
При этом в 1С вызовется процедура
Процедура ВнешнееСобытие(Источник, Событие, Данные)
Сообщить("Источник="+Источник);
Сообщить("Событие="+Событие);
Сообщить("Данные="+Данные);
КонецПроцедуры
Я постарался максимально приблизить синтаксис C# в 1С. И по мне, так даже в таком виде проще писать ВК на C#, чем использовать Native API на C++. При этом можно интегрировать использование .Net классов в 1С. Добавляя синонимы можно писать все на кириллице. Можно добавить поддержку итераторов итд.
Сейчас на Windows много всяких ActiveX, кроме того не сложно и самим написать COM компонент на любом языке.
Но это не кроссплатформенно.
Используя .Net Core и данную обертку над объектами .Net в 1С, можно значительно расширить возможности 1С, используя огромное количество библиотек и классов, в них находящихся. И сосредоточиться на разработку языка, наконец добавив замыкания, Linq, указание типа для intellisense (по аналогии с TypeScript). Разбиение модальных диалогов по аналогии с yeld и await в C#. А так же аналоги await для серверных вызовов с замыканиями.
Я понимаю, что данная разработка мало кому интересна. Но мне было интересно её разрабатывать. Кроме того будет интересна кто пробует .Net Core и использут Reflection
Примеры и исходники можно скачать Здесь
вы всегда так пишете? вас в команде один?
я видел вас закидали чем-то невкусным на хабреДа. На хабре кто кидает пережде всего реагируют на руслиш. Тема кроссплатформенности, и вызов .Net классов из натива им не интересна.