Zurück zur Startseite

Native AOT in .NET: Erfahrungen bei der Implementierung in CoreBus

Der Artikel beschreibt die praktische Erfahrung der Implementierung von Native AOT in der plattformübergreifenden .NET-Anwendung CoreBus. Wichtige Probleme werden betrachtet: kompilierte Bindings in Avalonia, Serialisierung ohne Reflection, Besonderheiten des plattformübergreifenden Builds, Trimming und falsche Antivirus-Positive.

CoreBus und Native AOT: echte Schwierigkeiten und Lösungen
Advertisement 728x90

CoreBus und Native AOT: Praxiserfahrungen beim Kompilieren eines plattformübergreifenden Terminals

Das Kompilieren von .NET-Anwendungen mit Native AOT verspricht schnellere Startzeiten und kleinere Verteilungspakete, stößt aber in der Praxis auf zahlreiche technische Einschränkungen. Dieser Artikel beleuchtet einen realen Fall der Integration von Native AOT in CoreBus – ein plattformübergreifendes Terminal zur Arbeit mit COM-Ports und Modbus-Protokollen.

Warum Native AOT?

Die Hauptgründe für den Wechsel waren langsame Kaltstarts auf Low-End-PCs und die große Installationspaketgröße. Nach der Migration von WPF zu Avalonia UI schrumpfte die App-Größe um ~60 MB, aber das 10-MB-Ziel schien immer noch unerreichbar. Native AOT erschien als logischer nächster Schritt, besonders für eine App, die auf Industriegeräte und eingebettete Systeme abzielt, wo Ressourcen oft knapp sind.

Allerdings wurde bereits im Build-Stadium klar, dass die Native-AOT-Unterstützung eine Überarbeitung der Bindings-Architektur, der Serialisierung und der Publishing-Strategie erforderte.

Google AdInline article slot

Problem 1: Vorkompilierte Bindings in Avalonia

Avalonia verwendet standardmäßig dynamische Bindings über Reflection, was nicht mit Native AOT kompatibel ist. Die Lösung besteht darin, vorkompilierte Bindings zu aktivieren:

<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>

Das bedeutet, den Datentyp in jeder XAML-Datei explizit mit dem x:DataType-Attribut am Root-Element (Window, UserControl) und in DataTemplates anzugeben. Fehlt dieses Attribut, kommt es entweder zu einem Kompilierfehler (Dynamic code generation is not supported on this platform) oder zu einem Absturz direkt nach dem Start.

Im CoreBus-Projekt erforderte das eine Aktualisierung aller XAML-Markups, was viel Zeit in Anspruch nahm, aber Laufzeitfehler eliminierte.

Google AdInline article slot

Problem 2: Serialisierung ohne Reflection

CoreBus-Einstellungen und Presets werden in JSON-Dateien mit System.Text.Json gespeichert. Die Standard-Serialisierungsmethoden sind mit RequiresUnreferencedCode und RequiresDynamicCode markiert, was während Native-AOT-Builds IL2026- und IL3050-Warnungen auslöst.

Die Lösung sind quellen-generierte JSON-Serialisierer über JsonSerializerContext. Hier ein Beispiel für eine korrekte Implementierung:

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);

Für die Deserialisierung ähnlich:

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

Das vermeidet Reflection vollständig und gewährleistet Kompatibilität mit Trimming und AOT.

Problem 3: Plattformübergreifende Builds

Native AOT erfordert native Kompilation für das Ziel-Betriebssystem und die Architektur. Die Linux-Kernel-Version und glibc sind entscheidend für die Kompatibilität:

  • Builds auf Ubuntu 24.10 (Kernel 6.11) liefen nicht auf Astra Linux CE (Kernel 5.15).
  • Builds auf Ubuntu 18.04 (glibc 2.27) scheiterten auf Astra Linux CE (glibc 2.24) wegen fehlender Abwärtskompatibilität in der C-Bibliothek.

Finale Strategie:

  • Windows: Build auf Windows 11, Test auf Windows 7/10 – erfolgreiche Kompatibilität.
  • Linux: Build direkt auf Astra Linux CE mit Kernel 5.4 und glibc 2.24.

Das maximiert die Portabilität, kompliziert aber den CI/CD-Prozess.

Problem 4: Trimming und abhängige Assemblies

Trimming (Entfernen ungenutzten Codes) bringt die größten Einsparungen nur im Haupt-Executable-Projekt. Hilfsbibliotheken ergeben insgesamt weniger als 300 KB Einsparung.

Aggressives Trimming kann jedoch wesentliche Avalonia-Ressourcen entfernen, insbesondere Styles. Es wird empfohlen, Themes explizit vom Trimming auszuschließen:

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

Während das in Avalonia 11.3.x möglicherweise nicht nötig ist, ist es ratsam, die Einstellung als Vorsichtsmaßnahme beizubehalten.

Problem 5: Falschpositive von Antiviren-Software

Apps, die mit Native AOT + Trimming gebaut wurden, werden oft als schädlich markiert. Windows Defender klassifizierte den Installer als Trojan:Win32/Bearfoos.B!ml und die portable Version als Trojan:Script/Wacatac.B!ml.

Das ist ein bekanntes Problem, das mit Änderungen in der Binärstruktur und fehlender digitaler Signatur zusammenhängt. Die Lösung besteht darin, die Microsoft SmartScreen-Verifizierung zu durchlaufen und eine digitale Signatur hinzuzufügen.

Wichtige Erkenntnisse

  • Native AOT erfordert vollständigen Verzicht auf Reflection und dynamische Code-Generierung.
  • Kompilation muss auf der Zielplattform erfolgen, unter Berücksichtigung von Kernel- und Systembibliotheksversionen.
  • Quellen-generierte Serialisierung ist für JSON-Handling zwingend erforderlich.
  • Trimming kann UI-Ressourcen beschädigen – schließen Sie Schlüssel-Assemblies manuell aus.
  • Antiviren-Tools reagieren oft mit Falschpositiven auf unsignierte AOT-Binaries.

— Editorial Team

Advertisement 728x90

Weiterlesen