CommunityCodierung & Entwicklunggithub.com

dotnet-project-structure

Modernes .NET-Projektstruktur-Setup mit .slnx-Format, Directory.Build.props, zentralem Paketmanagement, SourceLink, Versionsverwaltung mittels RELEASE_NOTES.md und SDK-Pinning per global.json.

Was ist dotnet-project-structure?

Dieses Skill beschreibt, wie man eine moderne .NET-Projektstruktur mit aktuellen Best Practices aufsetzt. Es deckt das neue XML-basierte `.slnx`-Lösungsformat ab, das ab .NET 9 verfügbar ist und gegenüber dem veralteten `.sln`-Format deutliche Vorteile bei Lesbarkeit, Versionskontrolle und Bearbeitbarkeit bietet. Zentrales Element ist die `Directory.Build.props`-Datei, die gemeinsame Build-Eigenschaften, C#-Spracheinstellungen, SourceLink für NuGet-Paket-Debugging und dynamische Copyright-Jahre definiert. Zusätzlich wird die zentrale Paketverwaltung (`Directory.Packages.props`) erklärt, um Abhängigkeitsversionen an einem Ort zu bündeln, sowie das Pinning der .NET SDK-Version per `global.json` für konsistente Builds in allen Umgebungen. Ein automatisiertes Versionsmanagement über `RELEASE_NOTES.md` und PowerShell-Skripte rundet das Skill ab. Das Skill ist primär für .NET 9+ und Visual Studio 17.13+ ausgelegt und richtet sich an Entwickler, die eine saubere, wartbare und CI/CD-freundliche Projektstruktur etablieren möchten.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Aaronontheweb/dotnet-skills/tree/main/skills/project-structure

Installed? Explore more Codierung & Entwicklung skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

.NET Projektstruktur und Build-Konfiguration

Wann dieses Skill verwendet werden sollte

Verwenden Sie dieses Skill, wenn:

  • Sie eine neue .NET-Lösung mit modernen Best Practices einrichten
  • Sie zentrale Build-Eigenschaften über mehrere Projekte hinweg konfigurieren möchten
  • Sie zentrale Paketversionsverwaltung implementieren möchten
  • Sie SourceLink für Debugging und NuGet-Pakete einrichten möchten
  • Sie die Versionsverwaltung mit Release Notes automatisieren möchten
  • Sie SDK-Versionen für konsistente Builds festlegen möchten

Verwandte Skills

  • dotnet-local-tools – Verwaltung lokaler .NET-Tools mit dotnet-tools.json
  • microsoft-extensions-configuration – Konfigurationsvalidierungsmuster

Lösungsdateiformat (.slnx)

Das .slnx-Format ist das moderne XML-basierte Lösungsdateiformat, das mit .NET 9 eingeführt wurde. Es ersetzt das traditionelle .sln-Format.

Vorteile gegenüber traditionellem .sln

Aspekt.sln (Legacy).slnx (Modern)
FormatBenutzerdefiniertes TextformatStandard XML
LesbarkeitGUIDs, kryptische SyntaxSauber, menschenlesbar
VersionskontrolleSchwer zu diffen/mergenLeicht zu diffen/mergen
BearbeitungIDE erforderlichJeder Texteditor

Versionsanforderungen

ToolMindestversion
.NET SDK9.0.200
Visual Studio17.13
MSBuildVisual Studio Build Tools 17.13

Hinweis: Ab .NET 10 erstellt dotnet new sln standardmäßig .slnx-Dateien. In .NET 9 müssen Sie explizit migrieren oder das Format angeben.

Beispiel einer .slnx-Datei

<Solution>
  <Folder Name="/build/">
    <File Path="Directory.Build.props" />
    <File Path="Directory.Packages.props" />
    <File Path="global.json" />
    <File Path="NuGet.Config" />
    <File Path="README.md" />
  </Folder>
  <Folder Name="/src/">
    <Project Path="src/MyApp/MyApp.csproj" />
    <Project Path="src/MyApp.Core/MyApp.Core.csproj" />
  </Folder>
  <Folder Name="/tests/">
    <Project Path="tests/MyApp.Tests/MyApp.Tests.csproj" />
  </Folder>
</Solution>

Migration von .sln zu .slnx

Verwenden Sie den Befehl dotnet sln migrate, um vorhandene Lösungen zu konvertieren:

# Eine bestimmte Lösungsdatei migrieren
dotnet sln MySolution.sln migrate

# Oder falls nur eine .sln im Verzeichnis existiert, einfach ausführen:
dotnet sln migrate

Wichtig: Behalten Sie nicht beide Dateien (.sln und .slnx) im selben Repository. Dies verursacht Probleme bei der automatischen Lösungsfindung und kann zu Synchronisationsproblemen führen. Löschen Sie nach der Migration die alte .sln-Datei.

Sie können auch in Visual Studio migrieren:

  1. Öffnen Sie die Lösung
  2. Wählen Sie die Lösung im Projektmappen-Explorer aus
  3. Gehen Sie zu Datei > Projektmappe speichern unter...
  4. Ändern Sie den Dateityp auf Xml-Projektmappendatei (*.slnx)

Erstellen einer neuen .slnx-Lösung

# .NET 10+: Erstellt standardmäßig .slnx
dotnet new sln --name MySolution

# .NET 9: Format explizit angeben
dotnet new sln --name MySolution --format slnx

# Projekte hinzufügen (funktioniert für beide Formate gleich)
dotnet sln add src/MyApp/MyApp.csproj

Empfehlung

Wenn Sie .NET 9.0.200 oder höher verwenden, migrieren Sie Ihre Lösungen zu .slnx. Die Vorteile sind erheblich:

  • Deutlich weniger Merge-Konflikte (keine zufälligen GUID-Änderungen)
  • Menschenlesbar und in jedem Texteditor bearbeitbar
  • Konsistent mit dem modernen .csproj-Format
  • Bessere Diff/Review-Erfahrung in Pull Requests

Directory.Build.props

Directory.Build.props bietet eine zentrale Build-Konfiguration, die für alle Projekte in einem Verzeichnisbaum gilt. Platzieren Sie sie im Lösungsstammverzeichnis.

Vollständiges Beispiel

<Project>
  <!-- Metadaten -->
  <PropertyGroup>
    <Authors>Ihr Team</Authors>
    <Company>Ihre Firma</Company>
    <!-- Dynamisches Copyright-Jahr – aktualisiert sich automatisch -->
    <Copyright>Copyright © 2020-$([System.DateTime]::Now.Year) Ihre Firma</Copyright>
    <Product>Ihr Produkt</Product>
    <PackageProjectUrl>https://github.com/yourorg/yourrepo</PackageProjectUrl>
    <RepositoryUrl>https://github.com/yourorg/yourrepo</RepositoryUrl>
    <PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
    <PackageTags>your;tags;here</PackageTags>
  </PropertyGroup>

  <!-- C#-Spracheinstellungen -->
  <PropertyGroup>
    <LangVersion>latest</LangVersion>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    <NoWarn>$(NoWarn);CS1591</NoWarn> <!-- Fehlende XML-Kommentare -->
  </PropertyGroup>

  <!-- Versionsverwaltung -->
  <PropertyGroup>
    <VersionPrefix>1.0.0</VersionPrefix>
    <PackageReleaseNotes>Siehe RELEASE_NOTES.md</PackageReleaseNotes>
  </PropertyGroup>

  <!-- Zielframework-Definitionen (wiederverwendbare Eigenschaften) -->
  <PropertyGroup>
    <NetStandardLibVersion>netstandard2.0</NetStandardLibVersion>
    <NetLibVersion>net8.0</NetLibVersion>
    <NetTestVersion>net9.0</NetTestVersion>
  </PropertyGroup>

  <!-- SourceLink-Konfiguration -->
  <PropertyGroup>
    <PublishRepositoryUrl>true</PublishRepositoryUrl>
    <EmbedUntrackedSources>true</EmbedUntrackedSources>
    <IncludeSymbols>true</IncludeSymbols>
    <SymbolPackageFormat>snupkg</SymbolPackageFormat>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
  </ItemGroup>

  <!-- NuGet-Paket-Assets -->
  <ItemGroup>
    <None Include="$(MSBuildThisFileDirectory)logo.png" Pack="true" PackagePath="\" />
    <None Include="$(MSBuildThisFileDirectory)README.md" Pack="true" PackagePath="\" />
  </ItemGroup>

  <PropertyGroup>
    <PackageIcon>logo.png</PackageIcon>
    <PackageReadmeFile>README.md</PackageReadmeFile>
  </PropertyGroup>

  <!-- Globale Using-Anweisungen -->
  <ItemGroup>
    <Using Include="System.Collections.Immutable" />
  </ItemGroup>
</Project>

Wichtige Muster

Dynamisches Copyright-Jahr

<Copyright>Copyright © 2020-$([System.DateTime]::Now.Year) Ihre Firma</Copyright>

Verwendet MSBuild-Eigenschaftenfunktionen, um das aktuelle Jahr zur Build-Zeit einzufügen. Keine manuellen Aktualisierungen erforderlich.

Wiederverwendbare Zielframework-Eigenschaften

Definieren Sie Zielframeworks einmal, referenzieren Sie sie überall:

<!-- In Directory.Build.props -->
<PropertyGroup>
  <NetLibVersion>net8.0</NetLibVersion>
  <NetTestVersion>net9.0</NetTestVersion>
</PropertyGroup>

<!-- In MyApp.csproj -->
<PropertyGroup>
  <TargetFramework>$(NetLibVersion)</TargetFramework>
</PropertyGroup>

<!-- In MyApp.Tests.csproj -->
<PropertyGroup>
  <TargetFramework>$(NetTestVersion)</TargetFramework>
</PropertyGroup>

SourceLink für NuGet-Pakete

SourceLink ermöglicht Schritt-für-Schritt-Debugging von NuGet-Paketen:

<PropertyGroup>
  <PublishRepositoryUrl>true</PublishRepositoryUrl>
  <EmbedUntrackedSources>true</EmbedUntrackedSources>
  <IncludeSymbols>true</IncludeSymbols>
  <SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>

<ItemGroup>
  <!-- Wählen Sie den richtigen Anbieter für Ihre Versionskontrolle -->
  <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
  <!-- Oder: Microsoft.SourceLink.AzureRepos.Git -->
  <!-- Oder: Microsoft.SourceLink.GitLab -->
  <!-- Oder: Microsoft.SourceLink.Bitbucket.Git -->
</ItemGroup>

Directory.Packages.props – Zentrale Paketverwaltung

Die zentrale Paketverwaltung (CPM) bietet eine einzige Quelle der Wahrheit für alle NuGet-Paketversionen.

Einrichtung

<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>

  <!-- Versionsvariablen für zusammengehörige Pakete definieren -->
  <PropertyGroup>
    <AkkaVersion>1.5.35</AkkaVersion>
    <AspireVersion>9.1.0</AspireVersion>
  </PropertyGroup>

  <!-- Anwendungsabhängigkeiten -->
  <ItemGroup Label="App Dependencies">
    <PackageVersion Include="Akka" Version="$(AkkaVersion)" />
    <PackageVersion Include="Akka.Cluster" Version="$(AkkaVersion)" />
    <PackageVersion Include="Akka.Persistence" Version="$(AkkaVersion)" />
    <PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
  </ItemGroup>

  <!-- Build-/Tooling-Abhängigkeiten -->
  <ItemGroup Label="Build Dependencies">
    <PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
  </ItemGroup>

  <!-- Testabhängigkeiten -->
  <ItemGroup Label="Test Dependencies">
    <PackageVersion Include="xunit" Version="2.9.3" />
    <PackageVersion Include="xunit.runner.visualstudio" Version="3.0.1" />
    <PackageVersion Include="FluentAssertions" Version="7.0.0" />
    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
    <PackageVersion Include="coverlet.collector" Version="6.0.3" />
  </ItemGroup>
</Project>

Pakete konsumieren (keine Version erforderlich)

<!-- In MyApp.csproj -->
<ItemGroup>
  <PackageReference Include="Akka" />
  <PackageReference Include="Akka.Cluster" />
  <PackageReference Include="Microsoft.Extensions.Hosting" />
</ItemGroup>

<!-- In MyApp.Tests.csproj -->
<ItemGroup>
  <PackageReference Include="xunit" />
  <PackageReference Include="FluentAssertions" />
  <PackageReference Include="Microsoft.NET.Test.Sdk" />
</ItemGroup>

Vorteile

  1. Einzelne Quelle der Wahrheit – Alle Versionen in einer Datei
  2. Kein Versionsdrift – Alle Projekte verwenden dieselben Versionen
  3. Einfache Aktualisierungen – Einmal ändern, überall wirksam
  4. Gruppierte Pakete – Versionsvariablen für zusammengehörige Pakete (z. B. alle Akka-Pakete)

global.json – SDK-Version festlegen

Legen Sie die .NET SDK-Version für konsistente Builds in allen Umgebungen fest.

{
  "sdk": {
    "version": "9.0.200",
    "rollForward": "latestFeature"
  }
}

Roll-Forward-Richtlinien

RichtlinieVerhalten
disableExakte Version erforderlich
patchGleiche Haupt-/Nebenversion, neuestes Patch
featureGleiche Hauptversion, neueste Nebenversion/Patch
latestFeatureGleiche Hauptversion, neuestes Feature-Band
minorGleiche Hauptversion, neueste Nebenversion
latestMinorGleiche Hauptversion, neueste Nebenversion
majorNeuestes SDK (nicht empfohlen)

Empfohlen: latestFeature – Erlaubt Patch-Updates innerhalb desselben Feature-Bands.


Versionsverwaltung mit RELEASE_NOTES.md

Format der Release Notes

#### 1.2.0 15. Januar 2025 ####

- Neue Funktion X hinzugefügt
- Fehler in Y behoben
- Leistung von Z verbessert

#### 1.1.0 10. Dezember 2024 ####

- Erste Veröffentlichung mit Funktionen A, B, C

Parsing-Skript (getReleaseNotes.ps1)

function Get-ReleaseNotes {
    param (
        [Parameter(Mandatory=$true)]
        [string]$MarkdownFile
    )

    $content = Get-Content -Path $MarkdownFile -Raw
    $sections = $content -split "####"

    $result = [PSCustomObject]@{
        Version      = $null
        Date         = $null
        ReleaseNotes = $null
    }

    if ($sections.Count -ge 3) {
        $header = $sections[1].Trim()
        $releaseNotes = $sections[2].Trim()

        $headerParts = $header -split " ", 2
        if ($headerParts.Count -eq 2) {
            $result.Version = $headerParts[0]
            $result.Date = $headerParts[1]
        }

        $result.ReleaseNotes = $releaseNotes
    }

    return $result
}

Versionserhöhungsskript (bumpVersion.ps1)

function UpdateVersionAndReleaseNotes {
    param (
        [Parameter(Mandatory=$true)]
        [PSCustomObject]$ReleaseNotesResult,
        [Parameter(Mandatory=$true)]
        [string]$XmlFilePath
    )

    $xmlContent = New-Object XML
    $xmlContent.Load($XmlFilePath)

    # VersionPrefix aktualisieren
    $versionElement = $xmlContent.SelectSingleNode("//VersionPrefix")
    $versionElement.InnerText = $ReleaseNotesResult.Version

    # PackageReleaseNotes aktualisieren
    $notesElement = $xmlContent.SelectSingleNode("//PackageReleaseNotes")
    $notesElement.InnerText = $ReleaseNotesResult.ReleaseNotes

    $xmlContent.Save($XmlFilePath)
}

Build-Skript (build.ps1)

# Hilfsskripte laden
. "$PSScriptRoot\scripts\getReleaseNotes.ps1"
. "$PSScriptRoot\scripts\bumpVersion.ps1"

# Release Notes parsen und Directory.Build.props aktualisieren
$releaseNotes = Get-ReleaseNotes -MarkdownFile (Join-Path -Path $PSScriptRoot -ChildPath "RELEASE_NOTES.md")
UpdateVersionAndReleaseNotes -ReleaseNotesResult $releaseNotes -XmlFilePath (Join-Path -Path $PSScriptRoot -ChildPath "Directory.Build.props")

Write-Output "Auf Version $($releaseNotes.Version) aktualisiert"

CI/CD-Integration

# GitHub Actions Beispiel
- name: Version aus Release Notes aktualisieren
  shell: pwsh
  run: ./build.ps1

- name: Build
  run: dotnet build -c Release

- name: Mit Tag-Version packen
  run: dotnet pack -c Release /p:PackageVersion=${{ github.ref_name }}

- name: Nach NuGet pushen
  run: dotnet nuget push **/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json

NuGet.Config

NuGet-Quellen und Verhalten konfigurieren:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <solution>
    <add key="disableSourceControlIntegration" value="true" />
  </solution>

  <packageSources>
    <clear />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
    <!-- Private Feeds bei Bedarf hinzufügen -->
    <!-- <add key="MyCompany" value="https://pkgs.dev.azure.com/myorg/_packaging/myfeed/nuget/v3/index.json" /> -->
  </packageSources>
</configuration>

Wichtige Einstellungen:

  • <clear /> – Entfernt geerbte/Standardquellen für reproduzierbare Builds
  • disableSourceControlIntegration – Verhindert TFS/Git-Integrationsprobleme

Vollständige Projektstruktur

MySolution/
├── .config/
│   └── dotnet-tools.json           # Lokale .NET-Tools
├── .github/
│   └── workflows/
│       ├── pr-validation.yml       # PR-Überprüfungen
│       └── release.yml             # NuGet-Veröffentlichung
├── scripts/
│   ├── getReleaseNotes.ps1         # RELEASE_NOTES.md parsen
│   └── bumpVersion.ps1             # Directory.Build.props aktualisieren
├── src/
│   ├── MyApp/
│   │   └── MyApp.csproj
│   └── MyApp.Core/
│       └── MyApp.Core.csproj
├── tests/
│   └── MyApp.Tests/
│       └── MyApp.Tests.csproj
├── Directory.Build.props           # Zentrale Build-Konfiguration
├── Directory.Packages.props        # Zentrale Paketverwaltung
├── global.json                     # SDK-Version festlegen
├── MySolution.slnx                 # Modernes Lösungsdateiformat
├── NuGet.Config                    # NuGet-Konfiguration
├── README.md                       # Projekt-Dokumentation
├── RELEASE_NOTES.md                # Release Notes
└── logo.png                        # Paket-Logo

Nächste Schritte

Nachdem Sie die Grundstruktur eingerichtet haben:

  1. Projektabhängigkeiten konfigurieren – Verweise zwischen Projekten hinzufügen, z. B. MyApp > MyApp.Core
  2. CI/CD-Pipelines einrichten – Build- und Test-Workflows mit GitHub Actions, Azure DevOps oder anderen
  3. NuGet-Pakete hinzufügen – Abhängigkeiten in Directory.Packages.props definieren
  4. Versionsstrategie einrichten – Release Notes pflegen und automatisierte Versionierung mittels build.ps1
  5. Dokumentation schreiben – README.md und weitere Dokumentation mit Projektbeschreibung und Architektur

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

akka-hosting-actor-patterns

Patterns for building entity actors with Akka.Hosting - GenericChildPerEntityParent, message extractors, cluster sharding abstraction, akka-reminders, and ITimeProvider. Supports both local testing and clustered production modes.

akka-net-aspire-configuration

Configure Akka.NET with .NET Aspire for local development and production deployments. Covers actor system setup, clustering, persistence, Akka.Management integration, and Aspire orchestration patterns.

akka-net-best-practices

Critical Akka.NET best practices including EventStream vs DistributedPubSub, supervision strategies, error handling, Props vs DependencyResolver, work distribution patterns, and cluster/local mode abstractions for testability.

akka-net-management

Akka.Management for cluster bootstrapping, service discovery (Kubernetes, Azure, Config), health checks, and dynamic cluster formation without static seed nodes.

akka-net-testing-patterns

Write unit and integration tests for Akka.NET actors using modern Akka.Hosting.TestKit patterns. Covers dependency injection, TestProbes, persistence testing, and actor interaction verification. Includes guidance on when to use traditional TestKit.

api-design

Design stable, compatible public APIs using extend-only design principles. Manage API compatibility, wire compatibility, and versioning for NuGet packages and distributed systems.

aspire-configuration

Configure Aspire AppHost to emit explicit app config via environment variables; keep app code free of Aspire clients and service discovery.

aspire-integration-testing

Write integration tests using .NET Aspire

aspire-service-defaults

Create a shared ServiceDefaults project for Aspire applications. Centralizes OpenTelemetry, health checks, resilience, and service discovery configuration across all services.

crap-analysis

Analyze code coverage and CRAP (Change Risk Anti-Patterns) scores to identify high-risk code. Use OpenCover format with ReportGenerator for Risk Hotspots showing cyclomatic complexity and untested code paths.

csharp-concurrency-patterns

Choosing the right concurrency abstraction in .NET - from async/await for I/O to Channels for producer/consumer to Akka.NET for stateful entity management. Avoid locks and manual synchronization unless absolutely necessary.

database-performance

Database access patterns for performance. Separate read/write models, avoid N+1 queries, use AsNoTracking, apply row limits, and never do application-side joins. Works with EF Core and Dapper.

dependency-injection-patterns

Organize DI registrations using IServiceCollection extension methods. Group related services into composable Add* methods for clean Program.cs and reusable configuration in tests.

dotnet-devcert-trust

Diagnose and fix .NET HTTPS dev certificate trust issues on Linux. Covers the full certificate lifecycle from generation to system CA bundle inclusion, with distro-specific guidance for Ubuntu, Fedora, Arch, and WSL2.

dotnet-local-tools

Managing local .NET tools with dotnet-tools.json for consistent tooling across development environments and CI/CD pipelines.

dotnet-slopwatch

Use Slopwatch to detect LLM reward hacking in .NET code changes. Run after every code modification to catch disabled tests, suppressed warnings, empty catch blocks, and other shortcuts that mask real problems.

efcore-patterns

Entity Framework Core best practices including NoTracking by default, query splitting for navigation collections, migration management, dedicated migration services, and common pitfalls to avoid.

ilspy-decompile

Understand implementation details of .NET code by decompiling assemblies. Use when you want to see how a .NET API works internally, inspect NuGet package source, view framework implementation, or understand compiled .NET binaries.

mailpit-integration

Test email sending locally using Mailpit with .NET Aspire. Captures all outgoing emails without sending them. View rendered HTML, inspect headers, and verify delivery in integration tests.

marketplace-publishing

Workflow for publishing skills and agents to the dotnet-skills Claude Code marketplace. Covers adding new content, updating plugin.json, validation, and release tagging.

Verwandte Skills