wshaddix/dotnet-skills1 file

Project Structure

Guidelines for organizing .NET projects, including solution structure, project references, folder conventions, .slnx format, centralized build properties, and central package management. Use when setting up a new .NET solution with modern best practices, configuring centralized build properties across multiple projects, implementing central package version management, or setting up SourceLink for debugging.

Specification
Skill ID
wshaddix/dotnet-skills/project-structure
Publisher
wshaddix
Repository
dotnet-skills
Installs
0
Files
1
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

wshaddix/dotnet-skills/project-structureInstalls these files
  • SKILL.md

What this skill tells the agent

.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

Recommended Solution Layout

MyApp/
├── .config/
│   └── dotnet-tools.json           # Local .NET tools
├── .editorconfig
├── .gitignore
├── global.json
├── nuget.config
├── Directory.Build.props
├── Directory.Build.targets
├── Directory.Packages.props
├── MyApp.slnx                       # .NET 9+ SDK / VS 17.13+
├── src/
│   ├── MyApp.Core/
│   │   └── MyApp.Core.csproj
│   ├── MyApp.Api/
│   │   ├── MyApp.Api.csproj
│   │   ├── Program.cs
│   │   └── appsettings.json
│   └── MyApp.Infrastructure/
│       └── MyApp.Infrastructure.csproj
└── tests/
    ├── MyApp.UnitTests/
    │   └── MyApp.UnitTests.csproj
    └── MyApp.IntegrationTests/
        └── MyApp.IntegrationTests.csproj

Key principles:

  • Separate src/ and tests/ directories
  • One project per concern (Core/Domain, Infrastructure, API/Host)
  • Solution file at the repo root
  • All shared build configuration at the repo root

Solution File Formats

.slnx (Modern — .NET 9+)

The XML-based solution format is human-readable and diff-friendly. Requires .NET 9+ SDK or Visual Studio 17.13+.

<Solution>
  <Folder Name="/build/">
    <File Path="Directory.Build.props" />
    <File Path="Directory.Packages.props" />
    <File Path="global.json" />
    <File Path="NuGet.Config" />
  </Folder>
  <Folder Name="/src/">
    <Project Path="src/MyApp.Core/MyApp.Core.csproj" />
    <Project Path="src/MyApp.Api/MyApp.Api.csproj" />
    <Project Path="src/MyApp.Infrastructure/MyApp.Infrastructure.csproj" />
  </Folder>
  <Folder Name="/tests/">
    <Project Path="tests/MyApp.UnitTests/MyApp.UnitTests.csproj" />
    <Project Path="tests/MyApp.IntegrationTests/MyApp.IntegrationTests.csproj" />
  </Folder>
</Solution>

Migrating from .sln to .slnx

dotnet sln MySolution.sln migrate

Important: Do not keep both .sln and .slnx files in the same repository.

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

dotnet sln add src/MyApp/MyApp.csproj

Benefits

  • Dramatically fewer merge conflicts
  • Human-readable and editable
  • Consistent with modern .csproj format
  • Better diff/review experience in pull requests

Directory.Build.props

Shared MSBuild properties applied to all projects in the directory subtree.

<Project>
  <!-- Metadata -->
  <PropertyGroup>
    <Authors>Your Team</Authors>
    <Company>Your Company</Company>
    <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>
  </PropertyGroup>

  <!-- C# Language Settings -->
  <PropertyGroup>
    <LangVersion>latest</LangVersion>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
    <AnalysisLevel>latest-all</AnalysisLevel>
    <NoWarn>$(NoWarn);CS1591</NoWarn>
  </PropertyGroup>

  <!-- Target Framework Definitions -->
  <PropertyGroup>
    <NetStandardLibVersion>netstandard2.0</NetStandardLibVersion>
    <NetLibVersion>net8.0</NetLibVersion>
    <NetTestVersion>net9.0</NetTestVersion>
  </PropertyGroup>

  <!-- SourceLink Configuration -->
  <PropertyGroup>
    <PublishRepositoryUrl>true</PublishRepositoryUrl>
    <EmbedUntrackSources>true</EmbedUntrackSources>
    <IncludeSymbols>true</IncludeSymbols>
    <SymbolPackageFormat>snupkg</SymbolPackageFormat>
    <DebugType>embedded</DebugType>
    <ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
  </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>

Nested Directory.Build.props

Inner files do not automatically import outer files:

<!-- src/Directory.Build.props -->
<Project>
  <Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
  <PropertyGroup>
    <!-- src-specific settings -->
  </PropertyGroup>
</Project>

Directory.Build.targets

Imported after project evaluation. Use for:

  • Shared analyzer package references
  • Custom build targets
  • Conditional logic based on project type
<Project>
  <ItemGroup>
    <PackageReference Include="Meziantou.Analyzer" PrivateAssets="all" />
    <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" PrivateAssets="all" />
  </ItemGroup>
</Project>

Directory.Packages.props - Central Package Management

CPM centralizes all NuGet package versions at the repo root. Individual .csproj files reference packages without a Version attribute.

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

  <PropertyGroup>
    <AkkaVersion>1.5.35</AkkaVersion>
    <AspireVersion>9.1.0</AspireVersion>
  </PropertyGroup>

  <ItemGroup Label="App Dependencies">
    <PackageVersion Include="Akka" Version="$(AkkaVersion)" />
    <PackageVersion Include="Akka.Cluster" Version="$(AkkaVersion)" />
    <PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
  </ItemGroup>

  <ItemGroup Label="Test Dependencies">
    <PackageVersion Include="xunit.v3" Version="3.2.2" />
    <PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
    <PackageVersion Include="FluentAssertions" Version="7.0.0" />
    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
    <PackageVersion Include="coverlet.collector" Version="8.0.0" />
  </ItemGroup>

  <ItemGroup Label="Build Dependencies">
    <PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
  </ItemGroup>
</Project>

Consuming Packages (No Version Needed)

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

Version Overrides

<PackageReference Include="Newtonsoft.Json" VersionOverride="13.0.3" />

.editorconfig

Place at the repo root to enforce consistent code style:

root = true

[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.{csproj,props,targets,xml,json,yml,yaml}]
indent_size = 2

[*.cs]
csharp_style_namespace_declarations = file_scoped:warning
csharp_prefer_braces = true:warning
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
dotnet_style_require_accessibility_modifiers = always:warning

Use this skill in a project

Open any RiverX project, search the Skills panel for wshaddix/dotnet-skills/project-structure, and install it in one click.

Use this skill