Files
soneta-erp-skills/soneta-programming/references/scan-modules.md
T
2026-05-20 18:26:26 +02:00

7.7 KiB

Skanowanie modułów i tabel z DLL (Roslyn MetadataReference)

Narzędzie do wylistowania wszystkich modułów (*Module) platformy enova365/Soneta oraz tabel (*Row / *Table) zdefiniowanych w każdym z nich. Czyta metadane skompilowanych bibliotek dodatku, nie wymaga źródeł.

Cel

W modelu Soneta każda baza danych jest opisana zbiorem modułów (HandelModule, KadryModule, CoreModule, …), a każdy moduł zawiera zagnieżdżone klasy *Row definiujące pojedyncze tabele. Skrypt pozwala szybko zinwentaryzować całą strukturę: jakie moduły są obecne w bibliotekach, jakie tabele zawierają i jak nazywa się klasa *Table używana w sesji (Session.Tables.*).

Używaj tego narzędzia, gdy:

  • eksplorujesz nieznany zestaw bibliotek i chcesz zobaczyć pełną listę modułów/tabel;
  • chcesz znaleźć właściwą nazwę RowType lub TableType przed użyciem skryptu scan-props;
  • przygotowujesz dodatek/raport, który potrzebuje pełnego mapowania klasa biznesowa ↔ nazwa tabeli;
  • weryfikujesz, że nowy dodatek został poprawnie zarejestrowany (jego *Module pojawia się na liście).

Mechanizm

Skrypt używa Roslyn (Microsoft.CodeAnalysis.CSharp) i MetadataReference.CreateFromFile, czyli metadane są czytane bez ładowania IL do CLR — bezpiecznie, bez ryzyka konfliktów wersji, x86/x64 itp.

Algorytm:

  1. Zbierz wszystkie *.dll z podanego katalogu i zarejestruj jako MetadataReference. Dodatkowo dołącz biblioteki runtime'u .NET z listy TPA (AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES")) — bez tego Roslyn nie rozwiązuje CaptionAttribute / DescriptionAttribute i ConstructorArguments zwraca pustą tablicę, przez co Tytuł/Opis zostają puste.
  2. Zbuduj CSharpCompilation z tymi referencjami.
  3. Przejdź rekurencyjnie po IAssemblySymbol.GlobalNamespace każdej referencji.
  4. Wybierz wszystkie publiczne klasy top-level o nazwie kończącej się na Module, które dziedziczą z Soneta.Business.Module (sprawdzane po BaseType). Filtr eliminuje "śmieci" w stylu System.Reflection.RuntimeModule.
  5. Dla każdego modułu:
    • znajdź zagnieżdżone klasy o nazwie kończącej się na Row (module.GetTypeMembers());
    • RowType = nazwa klasy bez sufiksu Row (np. DokumentHandlowyRowDokumentHandlowy);
    • TableType = nazwa typu property Table w klasie *Row (przeszukiwany wraz z dziedziczeniem przez FindMemberInherited);
    • Tytuł = CaptionAttribute, Opis = DescriptionAttribute z klasy *Table zagnieżdżonej w tym samym module (np. HandelModule.DokumentHandlowyTable). Atrybuty są deklarowane w l.mn. („Dokumenty handlowe"), bo opisują tabelę. Fallback: jeśli klasy *Table brak lub nie ma atrybutu, czytane są te same atrybuty z klasy *Row. Wartością jest pierwszy parametr string konstruktora atrybutu.
    • Guided — rozróżnia trzy stany:
      • root — klasa *Table dziedziczy (bezpośrednio lub pośrednio) z GuidedTable albo ExportedTable. Tabele te są korzeniami drzewa obiektów — stanowią root paczki danych (Datapack/GuidedRow/ExportedRow) i to one są obsługiwane przez mechanizm synchronizacji i eksportu/importu.
      • child: Pole→TypRow — tabela jest częścią drzewa innego rootu; pole rekordu z atrybutem [ColumnInfo(GuidedRelation=…)] wskazuje na tabelę nadrzędną. Pole to nazwa pola w *Record, TypRow to konkretny typ *Row odczytany z odpowiadającej property w klasie *Row (w *Record pole ma zwykle typ IRow).
      • pusta wartość — tabela szczegółowa (subrow, info-row) niewchodząca w skład żadnego drzewa guided.
    • Konfig = konfig, gdy *Table ma [TableInfo(IsConfig=true)]. Tabele konfiguracyjne żyją w osobnej sesji (ExecuteConfig) i mają inne reguły zapisu niż tabele operacyjne.
    • Interfaces = lista nazw interfejsów zadeklarowanych w [TableInfo(Interfaces = new[] { … })]. Soneta używa ich jako relacji interfejsowych — pole typu IXxx może referować rekord z dowolnej tabeli deklarującej IXxx w swoim TableInfo.
    • Dla samego modułu (*Module) Tytuł/Opis czytane są analogicznie z atrybutów na klasie modułu.
  6. Wypisz markdown: sekcja ## per moduł (z jego Caption/Description jeśli są), w każdej sekcji tabela RowType | TableType | Guided | Konfig | Interfaces | Tytuł | Opis.

Wymagania

  • .NET SDK (8.0+)
  • dotnet-script:
    dotnet tool install -g dotnet-script
    

Uruchomienie

dotnet script ~/.claude/skills/soneta-programming/scripts/scan-modules.csx \
    -- <KatalogDll>

Przykład

dotnet script ~/.claude/skills/soneta-programming/scripts/scan-modules.csx \
    -- ./bin/Debug/net8.0

Przykładowe wyjście

# Moduły i tabele (Soneta)

Znaleziono modułów: 37

## `Soneta.Handel.HandelModule`

- Opis: Moduł handlowy obsługujący dokumenty sprzedaży, zakupu, zamówień i innych operacji handlowych...
- Tabel: 62

| RowType | TableType | Guided | Konfig | Interfaces | Tytuł | Opis |
|---------|-----------|--------|--------|------------|-------|------|
| DefDokHandlowego | DefDokHandlowych | root | konfig |  | Definicje dokumentów handlowych | Konfigurowalna definicja (szablon) dokumentu handlowego... |
| DefRelacjiHandlowej | DefRelHandlowych | root | konfig |  | Definicje relacji handlowych | Konfigurowalna definicja relacji między dokumentami handlowymi... |
| DokumentHandlowy | DokHandlowe | root |  | IDokument, IKontrahentRef | Dokumenty handlowe | Główna tabela dokumentów handlowych (faktury, paragony, zamówienia, korekty, umowy itp.)... |
| DokumentHandlowyKoszt | DokHandloweKoszt | child: Dokument→DokumentHandlowy |  |  | Koszty dodatkowe | Koszt dodatkowy przypisany do dokumentu handlowego... |
| DrukarkaFiskalna | DrukarkiFiskalne | root | konfig |  | Lista drukarek fiskalnych | Konfiguracja drukarki fiskalnej... |
| ...  | ... | ... | ... | ... | ... | ... |

_Łącznie tabel: 1196_

Kody wyjścia

Kod Znaczenie
0 OK — wypisano listę modułów i tabel
1 Błąd argumentów / nie istnieje katalog / brak DLL

Ograniczenia

  • Skanuje tylko górny poziom katalogu (SearchOption.TopDirectoryOnly) — jeśli DLL są rozproszone, skopiuj je do jednego katalogu.
  • TableType dla abstrakcyjnych klas *Row (subrowy, klasy bazowe) jest często równy Table — to znaczy, że property Table pochodzi z klasy bazowej Soneta.Business.Row i zwraca ogólny typ Soneta.Business.Table, a klasa *Row nie ma własnej, dedykowanej tabeli.
  • Pierwsze uruchomienie pobiera pakiet NuGet Microsoft.CodeAnalysis.CSharp — wymaga połączenia internetowego (kolejne odpalenia działają offline).

Typowy workflow

  1. Wstępna inwentaryzacja — uruchom scan-modules.csx, żeby zobaczyć pełną listę RowType/TableType.
  2. Drążenie szczegółów — dla wybranego RowType (np. DokumentHandlowy) uruchom scan-props.csx i odczytaj listę pól bazodanowych oraz właściwości kalkulowanych klasy biznesowej.
  3. Generowanie kodu / form.xml / warunków — użyj odczytanych nazw i typów do budowania wyrażeń bindujących, warunków filtrujących, kodu workerów lub Datapacków.

Powiązania

  • scan-props.md — drugi skrypt skanujący, dla pojedynczego rekordu wypisuje pełną listę pól (bazodanowych + kalkulowanych) wraz z Tytuł/Opis i rekurencyjnym rozwinięciem subrowów.
  • Patrz skill soneta-business-xml — definicje schematu z których BusinessGenerator produkuje klasy *Module, *Row, *Table i *Record.