---
name: project-structure
description: 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.
---
# .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+.
```xml
```
### Migrating from .sln to .slnx
```bash
dotnet sln MySolution.sln migrate
```
**Important:** Do not keep both `.sln` and `.slnx` files in the same repository.
### Creating a New .slnx Solution
```bash
# .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.
```xml
Your Team
Your Company
Copyright © 2020-$([System.DateTime]::Now.Year) Your Company
Your Product
https://github.com/yourorg/yourrepo
https://github.com/yourorg/yourrepo
Apache-2.0
latest
enable
enable
true
true
latest-all
$(NoWarn);CS1591
netstandard2.0
net8.0
net9.0
true
true
true
snupkg
embedded
true
logo.png
README.md
```
### Nested Directory.Build.props
Inner files do **not** automatically import outer files:
```xml
```
---
## Directory.Build.targets
Imported **after** project evaluation. Use for:
- Shared analyzer package references
- Custom build targets
- Conditional logic based on project type
```xml
```
---
## 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.
```xml
true
true
1.5.35
9.1.0
```
### Consuming Packages (No Version Needed)
```xml
```
### Version Overrides
```xml
```
---
## .editorconfig
Place at the repo root to enforce consistent code style:
```ini
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
csharp_style_prefer_pattern_matching = true:suggestion
csharp_style_prefer_switch_expression = true:suggestion
csharp_using_directive_placement = outside_namespace:warning
dotnet_sort_system_directives_first = true
dotnet_naming_rule.private_fields_should_be_camel_case.symbols = private_fields
dotnet_naming_rule.private_fields_should_be_camel_case.style = camel_case_underscore
dotnet_naming_rule.private_fields_should_be_camel_case.severity = warning
dotnet_naming_symbols.private_fields.applicable_kinds = field
dotnet_naming_symbols.private_fields.applicable_accessibilities = private
dotnet_naming_style.camel_case_underscore.required_prefix = _
dotnet_naming_style.camel_case_underscore.capitalization = camel_case
```
---
## global.json - SDK Version Pinning
```json
{
"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.
---
## nuget.config
Configure package sources and security:
```xml
```
The `` + explicit sources + `` pattern prevents supply-chain attacks.
For private feeds:
```xml
```
---
## NuGet Audit
.NET 9+ enables `NuGetAudit` by default:
```xml
true
low
all
```
---
## Lock Files
Enable deterministic restores:
```xml
true
```
In CI:
```bash
dotnet restore --locked-mode
```
---
## SourceLink and Deterministic Builds
For libraries published to NuGet:
```xml
true
true
embedded
true
```
---
## Version Management with RELEASE_NOTES.md
```markdown
#### 1.2.0 January 15th 2025 ####
- Added new feature X
- Fixed bug in Y
#### 1.1.0 December 10th 2024 ####
- Initial release
```
### CI/CD Integration
```yaml
- 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 }}
```
---
## 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 |
| `.editorconfig` | Code style enforcement |
| `.config/dotnet-tools.json` | Local .NET tools |
---
## References
- [.NET Library Design Guidance](https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/)
- [Central Package Management](https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management)
- [.slnx Format](https://learn.microsoft.com/en-us/visualstudio/ide/reference/solution-file)
- [Directory.Build.props](https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build)
- [SourceLink](https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/sourcelink)
- [NuGet Audit](https://learn.microsoft.com/en-us/nuget/concepts/auditing-packages)