Powrót do strony głównej

Native AOT w .NET: doświadczenie wdrożenia w CoreBus

Artykuł opisuje praktyczne doświadczenie wdrożenia Native AOT w wieloplatformowej aplikacji .NET CoreBus. Omówiono kluczowe problemy: kompilowane wiązania w Avalonia, serializacja bez refleksji, cechy wieloplatformowego budowania, trimming i fałszywe alarmy antywirusów.

CoreBus i Native AOT: rzeczywiste trudności i rozwiązania
Advertisement 728x90

# CoreBus i Native AOT: praktyczne doświadczenie kompilacji wieloplatformowego terminala

Kompilacja aplikacji .NET z użyciem Native AOT obiecuje przyspieszenie uruchamiania i zmniejszenie rozmiaru dystrybucji, ale w praktyce wiąże się z wieloma ograniczeniami technicznymi. W artykule omawiany jest rzeczywisty przypadek integracji Native AOT w CoreBus — wieloplatformowym terminalu do pracy z portami COM i protokołami Modbus.

Dlaczego Native AOT?

Główne motywy przejścia — wolny zimny start na słabych PC i duży rozmiar pakietu instalacyjnego. Po migracji z WPF na Avalonia UI rozmiar aplikacji zmniejszył się o ~60 MB, jednak cel rzędu 10 MB pozostawał nieosiągalny. Native AOT wydawał się logicznym następnym krokiem, zwłaszcza dla aplikacji skierowanej na sprzęt przemysłowy i systemy wbudowane, gdzie zasoby są często ograniczone.

Jednak już na etapie budowania okazało się, że wsparcie Native AOT wymaga przebudowy architektury bindingów, serializacji i strategii publikacji.

Google AdInline article slot

Problem 1: kompilowane bindingi w Avalonia

Avalonia domyślnie używa dynamicznych bindingów poprzez refleksję, co jest niezgodne z Native AOT. Aby to rozwiązać, należy włączyć wstępnie skompilowane bindingi:

<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>

Wymaga to jawnego wskazania typu danych w każdym pliku XAML za pomocą atrybutu x:DataType na elemencie głównym (Window, UserControl) oraz wewnątrz DataTemplate. Brak tego atrybutu powoduje błąd kompilacji (Dynamic code generation is not supported on this platform) lub awarię aplikacji zaraz po uruchomieniu.

W projekcie CoreBus należało zmodyfikować wszystkie pliki XAML, co pochłonęło sporo czasu, ale pozwoliło uniknąć wyjątków w czasie wykonywania.

Google AdInline article slot

Problem 2: serializacja bez refleksji

Ustawienia i presety CoreBus są przechowywane w plikach JSON z użyciem System.Text.Json. Standardowe metody serializacji są oznaczone atrybutami RequiresUnreferencedCode i RequiresDynamicCode, co powoduje ostrzeżenia IL2026 i IL3050 podczas budowania z Native AOT.

Rozwiązanie — użycie source-generated JSON serializerów poprzez JsonSerializerContext. Przykład poprawnej implementacji:

var options = new JsonSerializerOptions
{
    WriteIndented = true,
    Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
    TypeInfoResolver = SerializerContext.Default
};

using var stream = new FileStream(correctFilePath, FileMode.Open);
var jsonTypeInfo = options.TypeInfoResolver.GetTypeInfo(typeof(T), options);

if (jsonTypeInfo == null)
{
    throw new Exception($"Not succeeded nayti type {typeof(T)} in obyavlenii konteksta serializatsii.");
}

JsonSerializer.Serialize(stream, data, jsonTypeInfo);

Do deserializacji stosuje się analogiczne podejście:

Google AdInline article slot
var data = (T?)JsonSerializer.Deserialize(stream, typeof(T), SerializerContext.Default);

To całkowicie eliminuje refleksję i gwarantuje zgodność z trymowaniem i AOT.

Problem 3: wieloplatformowa kompilacja

Native AOT wymaga natywnej kompilacji pod docelowy system operacyjny i architekturę. Wersja jądra Linux i glibc mają kluczowy wpływ na zgodność:

  • Kompilacja na Ubuntu 24.10 (jądro 6.11) nie uruchamiała się na Astra Linux CE (jądro 5.15).
  • Kompilacja na Ubuntu 18.04 (glibc 2.27) nie działała na Astra Linux CE (glibc 2.24) z powodu braku kompatybilności wstecznej biblioteki C.

Ostateczna strategia:

  • Windows: kompilacja na Windows 11, testy na Windows 7/10 — udana zgodność.
  • Linux: kompilacja bezpośrednio na Astra Linux CE z jądrem 5.4 i glibc 2.24.

To zapewnia maksymalną przenośność, ale komplikuje proces CI/CD.

Problem 4: trymowanie i zależne projekty

Trymowanie (usuwanie nieużywanego kodu) daje główny zysk tylko w głównym projekcie wykonywalnym. Biblioteki pomocnicze oszczędzają mniej niż 300 KB łącznie.

Jednak agresywne trymowanie może usunąć niezbędne zasoby Avalonia, zwłaszcza style. Zaleca się jawne wyłączenie motywów z trymowania:

<ItemGroup>
    <TrimmerRootAssembly Include="Avalonia.Themes.Fluent" />
</ItemGroup>

Chociaż w Avalonia 11.3.x może to nie być konieczne, lepiej zachować ustawienie jako zabezpieczenie.

Problem 5: fałszywe alarmy antywirusów

Aplikacje skompilowane z Native AOT + trymowanie często są rozpoznawane jako złośliwe. Windows Defender oznaczał instalator jako Trojan:Win32/Bearfoos.B!ml, a wersję przenośną — jako Trojan:Script/Wacatac.B!ml.

To znany problem związany ze zmianą struktury binarnej i brakiem podpisu cyfrowego. Rozwiązanie — przejście weryfikacji w Microsoft SmartScreen i dodanie podpisu cyfrowego.

Co ważne

  • Native AOT wymaga całkowitej rezygnacji z refleksji i dynamicznej generacji kodu.
  • Kompilacja musi być wykonywana na docelowej platformie z uwzględnieniem wersji jądra i bibliotek systemowych.
  • Source-generated serializacja jest obowiązkowa do pracy z JSON.
  • Trymowanie może uszkodzić zasoby UI — ręcznie wykluczaj kluczowe projekty.
  • Antywirusy często fałszywie alarmują na binarne AOT bez podpisu.

— Editorial Team

Advertisement 728x90

Czytaj dalej