Dynamiczne wywołanie kodu i zapisanie Expression do pliku

0

Cześć,
Chcę umożliwić użytkownikowi korzystającemu z aplikacji pisanie własnych funkcji, które będą miały dostęp do pewnych danych i będą wywoływane w pewnych sytuacjach. Dlatego, że piszę w blazor to funkcje mogłyby być pisane w JS, ale fajnie by było, gdyby można było pisać w C#. Pytanie tylko jak to zrobić?
Podobno Expression<T>, gdzie T to delegat można zapisać do pliku / bazy danych a następnie to wczytywać i dynamicznie wywoływać. Niestety nie mogę nigdzie znaleźć przykładu zapisania delegatu do pliku a następnie go odczytania w działającej aplikacji. Czy może ktoś potwierdzić lub skorygować moją wiedzę czy to jest do zrobienia?
Jakie mam inne alternatywy?

0

C# się kompiluje w locie, w RAM, i taką assembly z RAM runtime przyjmie, załaduje.
Szczegółów nie znam.

W sensie MOJEJ wypowiedzi, nie ma najmniejszego znaczenia co w tam jest, kod proceduralny (cokolwiek by to znaczyło) obiektowy czy funkcyjny

1

Dużo lepiej by było gdybyś wystawił interfejs programistyczny, i pozwolił żeby użytkownik sam skompilował i uruchomił aplikację.

Alternatywą byłoby:

  • Zespawnować virtualkę dla usera, i pozwolić mu na niej uruchomić nowy proces z kodem który wpisał
  • Użyć evala, kompilatora albo jakiegoś środowiska uruchomieniowego.
0

@Riddle Dzięki! A mógłbyś rozwinąć pomysł z virtualką? Jak miałoby to wyglądać?
Czy fakt, że aplikacja jest w blazorze (WASM) ma znaczenie?

0

podstawowe pytanie - chcesz to uruchamiać po stronie klienta czy serwera?

0
obscurity napisał(a):

podstawowe pytanie - chcesz to uruchamiać po stronie klienta czy serwera?

Po stronie klienta.

1
Kofcio napisał(a):

@Riddle Dzięki! A mógłbyś rozwinąć pomysł z virtualką? Jak miałoby to wyglądać?

No chcesz zrobić dla siebie Domain-Specific-Language w aplikacji. Jedyny powód czemu to według mnie miałoby sens, to żeby użytkownik mógl rozszerzyć samemu funkcjonalności aplikacji.

  • Ja zrobiłbym to wystawiając interfejs programistyczny, i myślę że to jest jedyny sensowny sposób zeby to zrobić
  • Ty zamiast tego wymyśliłeś że zrobisz DSL, co samo w sobie jest BARDZO trudne
    • Najlepszym wyjściem na to byłoby napisanie jakiegoś bardzo prostego języka, który ma tylko mały zbiór funkcjonalności
    • Ale Ty wmyśliłeś sobie że będzie to C#, czyli statycznie typowany i kompilowany język, z masą dodatkowych zależności i opcji konfiguracji.
    • Jedyny sposób, jaki teraz znam żeby odpalić taki kod, to jest przez dotneta, ale jesli pozwolisz użytkownikom uruchomienie randomowego kodu c# na swojej instacji, to nic nie stoi na przeszkodzie żeby ktoś Ci napisał kod który ci usunie C:\Windows\, więc nie możesz tego odpalić na swojej instacji, chyba że masz niesamowite zaufanie do swoich użytkowników, że nie zrobić nic głupiego przypadkiem ani specjalnie, co bezpiecznie można założyć że nie jest mądre; więc jedyna opcja jaka zostaje to uruchomić ten kod który ktoś Ci dośle na maszynie wirtualnej, musiałbyś z poziomu c# postawić maszynę wirtualną (np przez hyper-v, albo przez integrację z dockerem), zainstalować tam .NET'a (albo zrobić obraz już z .NET'em), wdrożyć na niego kod klienta, uruchomić, i zebrać wynik, no i obsłużyć wszystkie błędy które mogłyby z niego wyniknąć, przekazać pewnie też standard input i standard output. Bardzo dużo pracy

Czy fakt, że aplikacja jest w blazorze (WASM) ma znaczenie?

Nie specjalnie.

Kofcio napisał(a):
obscurity napisał(a):

podstawowe pytanie - chcesz to uruchamiać po stronie klienta czy serwera?

Po stronie klienta.

Po stronie klienta, czyli w przeglądarce? Musiałbyś napisać kompilator c#'a w JavaScript (albo znaleźć istniejący), skompilować ten kod i uruchomić go. Albo znaleźć środowisko uruchomieniowe c#'a w JS'ie. Wszystko to są bardzo trudne kwestie, więc odpalanie tego po stronie klienta raczej nie wchodzi w grę.

Jedyna opcja to uruchomienie tego po stronie servera, i wyświetlenie wyniku w przeglądarce.

0
Riddle napisał(a):
Kofcio napisał(a):

@Riddle Dzięki! A mógłbyś rozwinąć pomysł z virtualką? Jak miałoby to wyglądać?

No chcesz zrobić dla siebie Domain-Specific-Language w aplikacji. Jedyny powód czemu to według mnie miałoby sens, to żeby użytkownik mógl rozszerzyć samemu funkcjonalności aplikacji.

  • Ja zrobiłbym to wystawiając interfejs programistyczny, i myślę że to jest jedyny sensowny sposób zeby to zrobić
  • Ty zamiast tego wymyśliłeś że zrobisz DSL, co samo w sobie jest BARDZO trudne
  • (...)

@Riddle dzięki.
Zgadza się, chodzi o rozszerzenie funkcjonalności aplikacji.
Nie upieram się przy swoim rozwiązaniu - po prostu na chwilę obecną szukam najlepszego rozwiązania.

Pisząc o tym interfejsie programistycznym to masz cały czas na myśli, że to użytkownik skompiluje całą aplikację? Czy może jest sposób, żeby jakoś dołączyć do skompilowanej aplikacji kawałek kodu usera, który będzie implementował wspomniany interface?

0
Kofcio napisał(a):

Pisząc o tym interfejsie programistycznym to masz cały czas na myśli, że to użytkownik skompiluje całą aplikację? Czy może jest sposób, żeby jakoś dołączyć do skompilowanej aplikacji kawałek kodu usera, który będzie implementował wspomniany interface?

Najlepiej by było jakiś opisał dokładnie co chcesz zrobić - opisać jakiś przykład użycia konkretny, albo może widziałeś takie rozwiązanie już gdzieś?

0
Riddle napisał(a):
Kofcio napisał(a):

Pisząc o tym interfejsie programistycznym to masz cały czas na myśli, że to użytkownik skompiluje całą aplikację? Czy może jest sposób, żeby jakoś dołączyć do skompilowanej aplikacji kawałek kodu usera, który będzie implementował wspomniany interface?

Najlepiej by było jakiś opisał dokładnie co chcesz zrobić - opisać jakiś przykład użycia konkretny, albo może widziałeś takie rozwiązanie już gdzieś?

@Riddle
Aplikacja, którą piszę jest taką prostą aplikacją księgową. User może się zalogować i mieć wgląd do wprowadzonych dokumentów oraz może samodzielnie wprowadzać różnego rodzaju dokumenty. Chciałbym umożliwić userom tworzenie własnych funkcji, które przykładowo mogłyby automatyzować pewne czynności lub w inny sposób usprawnić pracę usera.
Funkcja byłaby przypisana do danego okna aplikacji i miałaby dostęp do danych, które są dostępne w danym oknie. Ewentualnie funkcja mogłaby korzystać z API aplikacji tj. mieć dostęp do innych danych do których ma dostęp user.

Przykład użycia takiej funkcji przez usera to przykładowo:
User może chcieć, żeby jakieś pole na danym formularzu było domyślnie zahaczone lub wypełnione w jakiś sposób - więc celem funkcji byłoby ustawienie danego pola na danym formularzu.
Można również wymusić aby jakieś pole było obowiązkowe.
Inny przykład: Jak mamy listę jakiś dokumentów to user może chcieć odfiltrować jakieś pozycje lub jakieś pozycje wyróżnić np. zmienić kolor kolumny / wiersza - według własnego widzimisię.
Inna opcja to chęć wyświetlenia jakiegoś raportu z danych dostępnych na stronie.
Jeszcze inny przykład: zrobić jakieś operacje seryjne na liście dokumentów.

Wiele z powyższych przykładów może być zaimplementowana bezpośrednio w aplikacji, ale chodzi o udostępnienie userowi narzędzia dzięki któremu sam będzie decydował co i jak ma być robione / zmieniane.

0
Kofcio napisał(a):
Riddle napisał(a):
Kofcio napisał(a):

Pisząc o tym interfejsie programistycznym to masz cały czas na myśli, że to użytkownik skompiluje całą aplikację? Czy może jest sposób, żeby jakoś dołączyć do skompilowanej aplikacji kawałek kodu usera, który będzie implementował wspomniany interface?

Najlepiej by było jakiś opisał dokładnie co chcesz zrobić - opisać jakiś przykład użycia konkretny, albo może widziałeś takie rozwiązanie już gdzieś?

@Riddle
Aplikacja, którą piszę jest taką prostą aplikacją księgową. User może się zalogować i mieć wgląd do wprowadzonych dokumentów oraz może samodzielnie wprowadzać różnego rodzaju dokumenty. Chciałbym umożliwić userom tworzenie własnych funkcji, które przykładowo mogłyby automatyzować pewne czynności lub w inny sposób usprawnić pracę usera.
Funkcja byłaby przypisana do danego okna aplikacji i miałaby dostęp do danych, które są dostępne w danym oknie. Ewentualnie funkcja mogłaby korzystać z API aplikacji tj. mieć dostęp do innych danych do których ma dostęp user.

Przykład użycia takiej funkcji przez usera to przykładowo:
User może chcieć, żeby jakieś pole na danym formularzu było domyślnie zahaczone lub wypełnione w jakiś sposób - więc celem funkcji byłoby ustawienie danego pola na danym formularzu.
Można również wymusić aby jakieś pole było obowiązkowe.
Inny przykład: Jak mamy listę jakiś dokumentów to user może chcieć odfiltrować jakieś pozycje lub jakieś pozycje wyróżnić np. zmienić kolor kolumny / wiersza - według własnego widzimisię.
Inna opcja to chęć wyświetlenia jakiegoś raportu z danych dostępnych na stronie.
Jeszcze inny przykład: zrobić jakieś operacje seryjne na liście dokumentów.

Wiele z powyższych przykładów może być zaimplementowana bezpośrednio w aplikacji, ale chodzi o udostępnienie userowi narzędzia dzięki któremu sam będzie decydował co i jak ma być robione / zmieniane.

Brzmi jakby wszystko to można było zrobić w Excelu.

0
Riddle napisał(a):

Brzmi jakby wszystko to można było zrobić w Excelu.

Tak, wg słów @Kofcio to nie są żadne ... np "reguły biznesowe", które wypełniają jakiś system (ERP, CRM itd), gdzie wypełniamy kod wdrożeniowy, dedykowany jednemu klientowi, ale w obrębie reguł wyższych, w tym z góry zaprojektowanych interfejsów.

A kolejne, że to wykonywane na kliencie

@Kofcio:
Co to NAPRAWDĘ ma być, bo więcej mi tu się "nie dodaje" niż to, co jest zrozumiałe

0
Kofcio napisał(a):

User może się zalogować i mieć wgląd do wprowadzonych dokumentów oraz może samodzielnie wprowadzać różnego rodzaju dokumenty. Chciałbym umożliwić userom tworzenie własnych funkcji, które przykładowo mogłyby automatyzować pewne czynności lub w inny sposób usprawnić pracę usera.
Funkcja byłaby przypisana do danego okna aplikacji i miałaby dostęp do danych, które są dostępne w danym oknie. Ewentualnie funkcja mogłaby korzystać z API aplikacji tj. mieć dostęp do innych danych do których ma dostęp user.

Przykład użycia takiej funkcji przez usera to przykładowo:
User może chcieć, żeby jakieś pole na danym formularzu było domyślnie zahaczone lub wypełnione w jakiś sposób - więc celem funkcji byłoby ustawienie danego pola na danym formularzu.
Można również wymusić aby jakieś pole było obowiązkowe.
Inny przykład: Jak mamy listę jakiś dokumentów to user może chcieć odfiltrować jakieś pozycje lub jakieś pozycje wyróżnić np. zmienić kolor kolumny / wiersza - według własnego widzimisię.
Inna opcja to chęć wyświetlenia jakiegoś raportu z danych dostępnych na stronie.
Jeszcze inny przykład: zrobić jakieś operacje seryjne na liście dokumentów.

Wiele z powyższych przykładów może być zaimplementowana bezpośrednio w aplikacji, ale chodzi o udostępnienie userowi narzędzia dzięki któremu sam będzie decydował co i jak ma być robione / zmieniane.

Wygląda jakbyś widział przez ramię coś takiego, ale nie rozumiał tego co najważniejsze.

Aplikacja, którą piszę jest taką prostą aplikacją księgową.

I co, suma Wn != suma Ma ?

3

Z tym skryptowaniem to w C# będzie więcej pracy i ci księgowi to raczej by powinni też umieć programować.

PoC zrobić można w ten sposób, musisz pobrać kompilator open source, wcześniej zapoznać się z implementacją CLI jego, żeby wykonać odpowiednik na stronie, zobaczyć jakie funkcje wywołuje, żeby wygenerować plik wynikowy.
Wszystkie te funkcje oznaczyć exported lub swoją jakąś wykonać.
Potem po kompilacji do wasm całego kompilatora, za pomocą tych wyexportowanych funkcji musisz napisać interface, w którym będzie można plik/kod źródłowy podać i wygenerować wasm'a.
Jakiś standard byś musiał napisać, żeby móc zautomatyzować wczytywanie takich modułów, każdy by musiał mieć zaimplementowaną jakąś funkcję do initializowania.

Coś z gotowców tego typu są w necie.

Ale lepiej po prostu użyć javascript i wyexportować sobie z blazera jakieś funkcje, potem w js możesz uruchamiać sobie te funkcje z wasm, nawet jest specjalny interface.
Teraz tak piszesz sobie jakieś ciekawe funkcje, a potem użytkownik do skryptowania dostanie dokumentacje, jak może pobrać dokumenty, dodać jakiegoś observera, wykonać filtrowania podając callback itp.

W sumie jako, że to strona internetowa i masz DOM strukturę, to nawet tego nie trzeba robić bo użytkownik może też manualnie sobie ze struktury strony zescrapować dane, modyfikować DOM.
Ale możesz mu jakiś prostszy interface przygotować https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/call-dotnet-from-javascript?view=aspnetcore-7.0
Teraz tylko jakoś musisz pozwolić wprowadzać skrypty, domyślnie jest tylko opcja przez konsolę F12, ale możesz dodać jakiś interface na stronie dla użytkownika i gdzieś składować jego skrypty, wypadało by też zadbać o kolorowanie składni i podpowiedzi.

0

@GodOfCode.:

Taaaa... enduser calback w JS ...

0

@GodOfCode. dziękuję za najbardziej merytoryczną odpowiedź :)
Księgowi raczej nie będą programować - to ma być tylko opcja dla bardziej technicznych osób :).
Po dłuższej analizie tematu również doszedłem do tego, że jednak JS to będzie najlepsze rozwiązanie.
Odnośnie "składowania" skryptów to zakładam, że będą one w bazie danych na serwerze i ładowane na stronę.

2

Pare miesięcy temu rozwiązywałem podobny problem, ale ostatecznie klient zrezygnował z tej funkcjonalności. Wtedy trafiłem na to repozytorium:

https://github.com/LostBeard/BlazorWASMScriptLoader

0

Dzięki. Wczoraj właśnie się zastanawiałem czy w blazorze jest opcja wykorzystania Roslyn-a ale jak widać tak :).
Mimo wszystko chyba jednak przynajmniej tymczasowo zostanę przy JS - będzie znacznie prościej :).

0

@GodOfCode. W jakim sensie backend miałby odpowiadać za kompilację? Chodzi Ci o to, aby całą aplikacją kompilować po stronie backendu wraz z kodem użytkownika? Jeśli tak to nie bardzo to widzę, bo to by oznaczało, że albo dany użytkownik miałby funkcje innych użytkowników albo musiałbym jakoś indywidualnie udostępniać skompilowane aplikacje każdemu użytkownikowi niezależnie (?).

Ja najchętniej to bym to widział tak, że użytkownik sam sobie pisze kod w swoim VS, następnie kompiluje kod do jakiejś dll-ki, która później jest dołączana do mojej aplikacji i wywoływana funkcja z tej dll-ki.
Tylko jakoś nie bardzo to widzę jak miałoby to dokładnie działać.

To o czym ja pisałem wcześniej to rozważałem, żeby kompilacja kodu użytkownika następowała w działającej już aplikacji, ale to mogłoby być zbyt problematyczne :-/.

3

Przykład kompilacji przy użyciu Roslyn + Blazor po stronie klienta:

https://wengier.com/SourceGeneratorPlayground/

aczkolwiek jest to możliwe przy użyciu starszych wersji Blazora, w nowszych wersjach jest problem z wczytywaniem zależności.

Istnieje też coś takiego jak Roslyn Scripting API, gdzie nie trzeba kompilować do assembly i bawić się w wczytywanie do pamieci, tylko można sobie kompilować lambdy i je wywoływać we własnym kodzie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1