Neu in .NET 10.0 [17]: NuGet-Pakete und Einstellungen fĂĽr File-based Apps

Informationen, die bei normalen .NET-Projekten in der Projektdatei .csproj liegen, setzt man in File-based Apps mit einer Präprozessor-Direktive.

vorlesen Druckansicht
C#-Zeichen

(Bild: Pincasso/Shutterstock)

Lesezeit: 2 Min.
Von
  • Dr. Holger Schwichtenberg
close notice

This article is also available in English. It was translated with technical assistance and editorially reviewed before publication.

Das direkte Ăśbersetzen und Starten von C#-Dateien nennt Microsoft File-based Apps. FĂĽr Informationen, die ĂĽblicherweise in der .csproj-Projektdatei liegen, hat Microsoft fĂĽr File-based Apps eine eigene Syntax eingefĂĽhrt.

Der Dotnet-Doktor – Holger Schwichtenberg
Der Dotnet-Doktor – Holger Schwichtenberg

Dr. Holger Schwichtenberg ist technischer Leiter des Expertennetzwerks www.IT-Visions.de, das mit 53 renommierten Experten zahlreiche mittlere und große Unternehmen durch Beratungen und Schulungen sowie bei der Softwareentwicklung unterstützt. Durch seine Auftritte auf zahlreichen nationalen und internationalen Fachkonferenzen sowie mehr als 90 Fachbücher und mehr als 1500 Fachartikel gehört Holger Schwichtenberg zu den bekanntesten Experten für .NET und Webtechniken in Deutschland.

Die Syntax beginnt mit der Raute # (eine Präprozessor-Direktive in C#) gefolgt von einem Doppelpunkt (aus der Sicht des C#-Compilers eine zu ignorierende Direktive):

  • Festlegung des SDKs: #:sdk Microsoft.NET.Sdk.Web. Bei der Angabe des SDK kann man ab Preview 6 auch die Versionsnummer nach @ angeben, beispielsweise #:sdk Aspire.AppHost.Sdk@9.3.1
  • Referenz auf ein NuGet-Paket: #:package Console@0.48.*
  • Referenz auf Projekte: #:project ./ClassLib/ClassLib.csproj
  • Build-Eigenschaften, z. B. Versionsnummer: #:property Version=1.1.2 (vor Preview 6 noch ohne Gleichheitszeichen, sondern mit Leerzeichen als Trennung)
  • File-based Apps verwenden im Standard den NativeAOT-Compiler. Wenn man ihn mit #:property PublishAot=false deaktiviert, wird der Just-in-Time-Compiler verwendet.

Auf NuGet.org gibt es inzwischen zu jedem Paket eine Registerkarte „File-based Apps“ mit der passenden Syntax zur Einbindung des Pakets in eine eigenständige C#-Datei (Abb. 1).

Videos by heise

Weitere Features von Files-based Apps sind:

  • Man kann eine Datei .settings.json im gleichen Ordner mit den Settings fĂĽr die File-based App anlegen.
  • Man kann in der Datei .run.json im gleichen Ordner ein Launch-Profil fĂĽr die File-based App anlegen.
  • Man kann dotnet build Dateiname.cs oder dotnet restore Dateiname.cs ausfĂĽhren.
  • Man kann solche File-based Apps mit dotnet publish cs zu einer ausfĂĽhrbaren Datei (.EXE) ĂĽbersetzen.
  • Innerhalb einer File-based App können Entwicklerinnen und Entwickler seit Preview 6 den Standort der Datei mit AppContext.GetData("EntryPointFileDirectoryPath") und den ganzen Pfad zur ausgefĂĽhrten C#-Datei mit GetData("EntryPointFilePath") bestimmen. Das funktioniert allerdings nur mit File-based Apps, nicht in normalen, projektbasierten C#-Anwendungen.

Es gibt aber in .NET 10.0 noch keine Möglichkeit, in einer File-based App eine andere .cs-Datei direkt einzubinden. Das ist für .NET 11.0 geplant.

Folgender Code zeigt ein Beispiel einer File-based App mit zwei referenzierten NuGet-Paketen:

#!/usr/bin/env dotnet
#region Einstellungen fĂĽr File-based App
// https://www.nuget.org/packages/humanizer/
#:package Humanizer@2.14.1 
// https://www.nuget.org/packages/Spectre.Console/
#:package Spectre.Console@0.*
#:property LangVersion=preview
#:property Version=1.2.0
#:project ./ClassLibrary/ClassLibrary.csproj
#endregion
using Spectre.Console;
using Humanizer;

var title = "C# Script v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version +
" mit " + System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription + "\n" +
AppContext.GetData("EntryPointFilePath");

// Header
AnsiConsole.Write(
    new Panel(title)
        .Header("[yellow]File-based App[/]", Justify.Center)
        .Border(BoxBorder.Rounded)
        .BorderStyle(new Style(foreground: Color.Green))
        .Padding(1, 1, 1, 1)
);

// Parameter auflisten
foreach (var arg in args)
{
    Console.WriteLine($"Argument: {arg}");
}

Console.WriteLine();

// Daten
(Data net80, Data net90, Data net10) = GetData();

// Textausgabe in Wochen
var dotNet8Released = DateTimeOffset.Parse(net80.Release);
TimeSpan dotNet8Since = DateTimeOffset.Now - dotNet8Released;
Console.WriteLine($"It has been {dotNet8Since.Humanize()} since .NET {net80.Version} was released.");

var dotNet9Released = DateTimeOffset.Parse(net90.Release);
TimeSpan dotNet9Since = DateTimeOffset.Now - dotNet9Released;
Console.WriteLine($"It has been {dotNet9Since.Humanize()} since .NET {net90.Version} was released.");

var dotNet10Released = DateTimeOffset.Parse(net10.Release);
TimeSpan dotNet10Since = DateTimeOffset.Now - dotNet10Released;
Console.WriteLine($"{dotNet10Since.Humanize()} since .NET {net10.Version} release.");

Console.WriteLine();

// Zeichne Balken für die Anzahl der Tage seit der Veröffentlichung
var bar = new BarChart()
    .Width(100)
    .AddItem("Days since .NET 8.0 release", dotNet8Since.TotalDays, Color.Red)
    .AddItem("Days since .NET 9.0 release", dotNet9Since.TotalDays, Color.Blue)
    .AddItem("Days since .NET 10.0 release", dotNet10Since.TotalDays, Color.Purple);
AnsiConsole.Write(bar);

Console.WriteLine();

// Lokale Funktion
static (Data, Data, Data) GetData()
{
    var net80 = new Data
    {
        Version = "8.0",
        Release = "2023-11-14"
    };
    var net90 = new Data
    {
        Version = "9.0",
        Release = "2024-11-12"
    };
    var net10 = new Data
    {
        Version = "10.0",
        Release = "2025-11-11"
    };
    return (net80, net90, net10);
}

// Datenklasse
class Data
{
    public required string Version { get; set; }
    public string Release { get; set; }
}

Der Screenshot zeigt die Ausgabe beim Starten des Beispielcodes (Abb. 2).

(rme)