
dotnet-project-structure
Modern .NET project structure including .slnx solution format, Directory.Build.props, central package management, SourceLink, version management with RELEASENOTES.md, and SDK pinning with global.json.
- Installs
- 666
- Last 24h
- +8
- Trending rank
- #9638
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/aaronontheweb/dotnet-skills --skill dotnet-project-structure- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads dotnet-project-structure automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
.NET Project Structure and Build Configuration
When to Use This Skill
Use this skill when:
- Setting up a new .NET solution with modern best practices
- Configuring centralized build properties across multiple projects
- Implementing central package version management
- Setting up SourceLink for debugging and NuGet packages
- Automating version management with release notes
- Pinning SDK versions for consistent builds
Related Skills
dotnet-local-tools- Managing local .NET tools with dotnet-tools.jsonmicrosoft-extensions-configuration- Configuration validation patterns
Solution File Format (.slnx)
The .slnx format is the modern XML-based solution file format introduced in .NET 9. It replaces the traditional .sln format.
Benefits Over Traditional .sln
| Aspect | .sln (Legacy) | .slnx (Modern) |
|---|---|---|
| Format | Custom text format | Standard XML |
| Readability | GUIDs, cryptic syntax | Clean, human-readable |
| Version control | Hard to diff/merge | Easy to diff/merge |
| Editing | IDE required | Any text editor |
Version Requirements
| Tool | Minimum Version |
|---|---|
| .NET SDK | 9.0.200 |
| Visual Studio | 17.13 |
| MSBuild | Visual Studio Build Tools 17.13 |
Note: Starting with .NET 10, dotnet new sln creates .slnx files by default. In .NET 9, you must explicitly migrate or specify the format.
Example .slnx File
<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>
Migrating from .sln to .slnx
Use the dotnet sln migrate command to convert existing solutions:
# Migrate a specific solution file
dotnet sln MySolution.sln migrate
# Or if only one .sln exists in the directory, just run:
dotnet sln migrate
Important: Do not keep both .sln and .slnx files in the same repository. This causes issues with automatic solution detection and can lead to sync problems. After migration, delete the old .sln file.
You can also migrate in Visual Studio:
- Open the solution
- Select the Solution in Solution Explorer
- Go to File > Save Solution As...
- Change "Save as type" to Xml Solution File (*.slnx)
Creating a New .slnx Solution
# .NET 10+: Creates .slnx by default
dotnet new sln --name MySolution
# .NET 9: Specify the format explicitly
dotnet new sln --name MySolution --format slnx
# Add projects (works the same for both formats)
dotnet sln add src/MyApp/MyApp.csproj
Recommendation
If you're using .NET 9.0.200 or later, migrate your solutions to .slnx. The benefits are significant:
- Dramatically fewer merge conflicts (no random GUIDs changing)
- Human-readable and editable in any text editor
- Consistent with modern
.csprojformat - Better diff/review experience in pull requests
Directory.Build.props
Directory.Build.props provides centralized build configuration that applies to all projects in a directory tree. Place it at the solution root.
Complete Example
<Project>
<!-- Metadata -->
<PropertyGroup>
<Authors>Your Team</Authors>
<Company>Your Company</Company>
<!-- Dynamic copyright year - updates automatically -->
<Copyright>Copyright © 2020-$([System.DateTime]::Now.Year) Your Company</Copyright>
<Product>Your Product</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# Language Settings -->
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- Missing XML comments -->
</PropertyGroup>
<!-- Version Management -->
<PropertyGroup>
<VersionPrefix>1.0.0</VersionPrefix>
<PackageReleaseNotes>See RELEASE_NOTES.md</PackageReleaseNotes>
</PropertyGroup>
<!-- Target Framework Definitions (reusable properties) -->
<PropertyGroup>
<NetStandardLibVersion>netstandard2.0</NetStandardLibVersion>
<NetLibVersion>net8.0</NetLibVersion>
<NetTestVersion>net9.0</NetTestVersion>
</PropertyGroup>
<!-- SourceLink Configuration -->
<PropertyGroup>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
</ItemGroup>
<!-- NuGet Package 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>
<!-- Global Using Statements -->
<ItemGroup>
<Using Include="System.Collections.Immutable" />
</ItemGroup>
</Project>
Key Patterns
Dynamic Copyright Year
<Copyright>Copyright © 2020-$([System.DateTime]::Now.Year) Your Company</Copyright>
Uses MSBuild property functions to insert current year at build time. No manual updates needed.
Reusable Target Framework Properties
Define target frameworks once, reference everywhere:
<!-- 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 for NuGet Packages
SourceLink enables step-through debugging of NuGet packages:
<PropertyGroup>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
<ItemGroup>
<!-- Choose the right provider for your source control -->
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
<!-- Or: Microsoft.SourceLink.AzureRepos.Git -->
<!-- Or: Microsoft.SourceLink.GitLab -->
<!-- Or: Microsoft.SourceLink.Bitbucket.Git -->
</ItemGroup>
Directory.Packages.props - Central Package Management
Central Package Management (CPM) provides a single source of truth for all NuGet package versions.
Setup
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<!-- Define version variables for related packages -->
<PropertyGroup>
<AkkaVersion>1.5.35</AkkaVersion>
<AspireVersion>9.1.0</AspireVersion>
</PropertyGroup>
<!-- Application Dependencies -->
<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 Dependencies -->
<ItemGroup Label="Build Dependencies">
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
</ItemGroup>
<!-- Test Dependencies -->
<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>
Consuming Packages (No Version Needed)
<!-- 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>
Benefits
- Single source of truth - All versions in one file
- No version drift - All projects use same versions
- Easy updates - Change once, applies everywhere
- Grouped packages - Version variables for related packages (e.g., all Akka packages)
global.json - SDK Version Pinning
Pin the .NET SDK version for consistent builds across all environments.
{
"sdk": {
"version": "9.0.200",
"rollForward": "latestFeature"
}
}
Roll Forward Policies
| Policy | Behavior |
|---|---|
disable | Exact version required |
patch | Same major.minor, latest patch |
feature | Same major, latest minor.patch |
latestFeature | Same major, latest feature band |
minor | Same major, latest minor |
latestMinor | Same major, latest minor |
major | Latest SDK (not recommended) |
Recommended: latestFeature - Allows patch updates within the same feature band.
Version Management with RELEASE_NOTES.md
Release Notes Format
#### 1.2.0 January 15th 2025 ####
- Added new feature X
- Fixed bug in Y
- Improved performance of Z
#### 1.1.0 December 10th 2024 ####
- Initial release with features A, B, C
Parsing Script (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
}
Version Bump Script (bumpVersion.ps1)
function UpdateVersionAndReleaseNotes {
param (
[Parameter(Mandatory=$true)]
[PSCustomObject]$ReleaseNotesResult,
[Parameter(Mandatory=$true)]
[string]$XmlFilePath
)
$xmlContent = New-Object XML
$xmlContent.Load($XmlFilePath)
# Update VersionPrefix
$versionElement = $xmlContent.SelectSingleNode("//VersionPrefix")
$versionElement.InnerText = $ReleaseNotesResult.Version
# Update PackageReleaseNotes
$notesElement = $xmlContent.SelectSingleNode("//PackageReleaseNotes")
$notesElement.InnerText = $ReleaseNotesResult.ReleaseNotes
$xmlContent.Save($XmlFilePath)
}
Build Script (build.ps1)
# Load helper scripts
. "$PSScriptRoot\scripts\getReleaseNotes.ps1"
. "$PSScriptRoot\scripts\bumpVersion.ps1"
# Parse release notes and update Directory.Build.props
$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 "Updated to version $($releaseNotes.Version)"
CI/CD Integration
# GitHub Actions example
- name: Update version from release notes
shell: pwsh
run: ./build.ps1
- name: Build
run: dotnet build -c Release
- name: Pack with tag version
run: dotnet pack -c Release /p:PackageVersion=${{ github.ref_name }}
- name: Push to NuGet
run: dotnet nuget push **/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json
NuGet.Config
Configure NuGet sources and behavior:
<?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" />
<!-- Add private feeds if needed -->
<!-- <add key="MyCompany" value="https://pkgs.dev.azure.com/myorg/_packaging/myfeed/nuget/v3/index.json" /> -->
</packageSources>
</configuration>
Key Settings:
<clear />- Remove inherited/default sources for reproducible buildsdisableSourceControlIntegration- Prevents TFS/Git integration issues
Complete Project Structure
MySolution/
├── .config/
│ └── dotnet-tools.json # Local .NET tools
├── .github/
│ └── workflows/
│ ├── pr-validation.yml # PR checks
│ └── release.yml # NuGet publishing
├── scripts/
│ ├── getReleaseNotes.ps1 # Parse RELEASE_NOTES.md
│ └── bumpVersion.ps1 # Update Directory.Build.props
├── src/
│ ├── MyApp/
│ │ └── MyApp.csproj
│ └── MyApp.Core/
│ └── MyApp.Core.csproj
├── tests/
│ └── MyApp.Tests/
│ └── MyApp.Tests.csproj
├── Directory.Build.props # Centralized build config
├── Directory.Packages.props # Central package versions
├── MySolution.slnx # Modern solution file
├── global.json # SDK version pinning
├── NuGet.Config # Package source config
├── build.ps1 # Build orchestration
├── RELEASE_NOTES.md # Version history
├── README.md # Project documentation
└── logo.png # Package icon
Quick Reference
| File | Purpose |
|---|---|
MySolution.slnx | Modern XML solution file |
Directory.Build.props | Centralized build properties |
Directory.Packages.props | Central package version management |
global.json | SDK version pinning |
NuGet.Config | Package source configuration |
RELEASE_NOTES.md | Version history (parsed by build) |
build.ps1 | Build orchestration script |
.config/dotnet-tools.json | Local .NET tools |
FAQ
What does the dotnet-project-structure skill do?
Modern .NET project structure including .slnx solution format, Directory.Build.props, central package management, SourceLink, version management with RELEASENOTES.md, and SDK pinning with global.json.
How do I install dotnet-project-structure?
Run npx skills add https://github.com/aaronontheweb/dotnet-skills --skill dotnet-project-structure in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/dotnet-project-structure/ (personal) or .claude/skills/dotnet-project-structure/ (project).
Which AI agents can use dotnet-project-structure?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is dotnet-project-structure safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains dotnet-project-structure?
It is published by aaronontheweb in aaronontheweb/dotnet-skills. It has 666 recorded installs.
More from aaronontheweb/dotnet-skills
modern-csharp-coding-standards
aaronontheweb/dotnet-skills
Write modern, high-performance C code using records, pattern matching, value objects, async/await, Span /Memory , and best-practice API design patterns. Emphasizes functional-style programming with C 12+ features.
csharp-concurrency-patterns
aaronontheweb/dotnet-skills
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.
efcore-patterns
aaronontheweb/dotnet-skills
Entity Framework Core best practices including NoTracking by default, query splitting for navigation collections, migration management, dedicated migration services, and common pitfalls to avoid.
dependency-injection-patterns
aaronontheweb/dotnet-skills
Organize DI registrations using IServiceCollection extension methods. Group related services into composable Add methods for clean Program.cs and reusable configuration in tests.
database-performance
aaronontheweb/dotnet-skills
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.
api-design
aaronontheweb/dotnet-skills
Design stable, compatible public APIs using extend-only design principles. Manage API compatibility, wire compatibility, and versioning for NuGet packages and distributed systems.
Popular backend & apis skills
find-skills
vercel-labs/skills
Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
entra-app-registration
microsoft/azure-skills
Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Key Vault secrets (use azure-keyvault-expiration-audit), general Azure resource security guidance.
google-agents-cli-adk-code
google/agents-cli
This skill should be used when the user wants to "write agent code", "build an agent with ADK", "add a tool", "create a callback", "define an agent", "use state management" — in a project that needs ADK (Agent Development Kit) API patterns and code examples. It provides a quick reference for agent types, tool definitions, orchestration patterns, callbacks, state management, the graph Workflow API, and reference recipes to study. Do NOT use for scaffolding (use google-agents-cli-scaffold) or deployment (use google-agents-cli-deploy).
embedded-captions
heygen-com/hyperframes
Add captions or subtitles to an existing single-subject talking-head video without editing the footage. Use for plain verbatim captions, cinematic captions embedded behind the subject, VFX captions, “炸/特效/酷炫字幕,” or a named identity from the 35-style catalog. Route by visual identity, not by backend engine. The quiet anchor rail is the default; embed every word only when the user explicitly wants a fully cinematic treatment. The workflow runs locally end to end, including transcription and subject matting; split multi-shot footage before applying it.
entra-agent-id
microsoft/azure-skills
Provision Microsoft Entra Agent Identity Blueprints, BlueprintPrincipals, and per-instance Agent Identities via Microsoft Graph, and configure OAuth 2.0 token exchange (fmipath, OBO, cross-tenant) including the Microsoft Entra SDK for AgentID sidecar. USE FOR: Agent Identity Blueprint, BlueprintPrincipal, agent OAuth, fmipath token exchange, agent OBO, Workload Identity Federation for agents, polyglot agent auth, Microsoft.Identity.Web.AgentIdentities. DO NOT USE FOR: standard Entra app registration (use entra-app-registration), Microsoft Foundry agent authoring (use microsoft-foundry).
neon
neondatabase/agent-skills
Overview of Neon, a complete set of cloud backend primitives around Lakebase Postgres: Auth, Object Storage, Functions, the AI Gateway, and Realtime. Start here to choose Neon for undecided login, files, APIs, and LLM calls, set up the CLI or MCP server, and follow the branch-first workflow. Use when building an app or backend on Neon, or when "Neon" or "Lakebase Postgres" is mentioned. Child skill neon-postgres wins for an existing DATABASEURL, SQL, schema, inspect, or search. Child skill neon-auth wins for login, users, sessions, identity routing, and Managed Better Auth setup. Child skill neon-realtime wins for live queries in reactive apps and agents. Also use for object storage, S3, buckets, serverless functions, function triggers, cron, AI gateway, LLM calls, realtime, logs, Loki, Grafana, observability, postgres, database, backend, Claimable Neon, neon.new, or a no-signup database.