dotnet-scaffold-project
dotnet-scaffold-project
Scaffolds a new .NET project with all modern best practices applied. Generates the full solution structure including Central Package Management, analyzers, .editorconfig, SourceLink, and deterministic builds.
Prerequisites: Run [skill:dotnet-version-detection] first to determine available SDK version — this affects which features and templates are available.
Scope
- Full solution structure generation (src/, tests/, .sln)
- Central Package Management and Directory.Build.props
- Analyzers, .editorconfig, SourceLink, deterministic builds
Out of scope
- Solution layout rationale and conventions -- see [skill:dotnet-project-structure]
- Analyzer configuration details -- see [skill:dotnet-add-analyzers]
- CI workflow generation -- see [skill:dotnet-add-ci]
Cross-references: [skill:dotnet-project-structure] for layout rationale, [skill:dotnet-add-analyzers] for analyzer configuration, [skill:dotnet-add-ci] for adding CI after scaffolding.
Step 1: Create Solution Structure
Create the directory layout and solution file.
# Create the directory structure
mkdir -p MyApp/src MyApp/tests
# Create solution file
cd MyApp
dotnet new sln -n MyApp
# For .NET 9+ SDK, convert to .slnx
dotnet sln MyApp.sln migrate
Choose Project Template
Select the appropriate template based on the application type:
| Template | Command | SDK |
|---|---|---|
| Web API (minimal) | dotnet new webapi -n MyApp.Api -o src/MyApp.Api |
Microsoft.NET.Sdk.Web |
| Web API (controllers) | dotnet new webapi -n MyApp.Api -o src/MyApp.Api --use-controllers |
Microsoft.NET.Sdk.Web |
| Console app | dotnet new console -n MyApp.Cli -o src/MyApp.Cli |
Microsoft.NET.Sdk |
| Worker service | dotnet new worker -n MyApp.Worker -o src/MyApp.Worker |
Microsoft.NET.Sdk.Worker |
| Class library | dotnet new classlib -n MyApp.Core -o src/MyApp.Core |
Microsoft.NET.Sdk |
| Blazor web app | dotnet new blazor -n MyApp.Web -o src/MyApp.Web |
Microsoft.NET.Sdk.Web |
| MAUI app | dotnet new maui -n MyApp.Mobile -o src/MyApp.Mobile |
Microsoft.Maui.Sdk |
| xUnit test | dotnet new xunit -n MyApp.Tests -o tests/MyApp.Tests |
Microsoft.NET.Sdk |
# Example: Web API with class library and tests
dotnet new classlib -n MyApp.Core -o src/MyApp.Core
dotnet new webapi -n MyApp.Api -o src/MyApp.Api
dotnet new xunit -n MyApp.UnitTests -o tests/MyApp.UnitTests
# Add projects to solution
dotnet sln add src/MyApp.Core/MyApp.Core.csproj
dotnet sln add src/MyApp.Api/MyApp.Api.csproj
dotnet sln add tests/MyApp.UnitTests/MyApp.UnitTests.csproj
# Add project references
dotnet add src/MyApp.Api/MyApp.Api.csproj reference src/MyApp.Core/MyApp.Core.csproj
dotnet add tests/MyApp.UnitTests/MyApp.UnitTests.csproj reference src/MyApp.Core/MyApp.Core.csproj
Step 2: Add global.json
Pin the SDK version for reproducible builds.
{
"sdk": {
"version": "10.0.100",
"rollForward": "latestPatch"
}
}
Adjust the version to match the output of dotnet --version.
Step 3: Add Directory.Build.props
Create at the repo root to share build settings across all projects.
<Project>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>14</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<AnalysisLevel>latest-all</AnalysisLevel>
</PropertyGroup>
<!-- Deterministic builds and SourceLink (for libraries) -->
<PropertyGroup>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<DebugType>embedded</DebugType>
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
</PropertyGroup>
<!-- NuGet audit -->
<PropertyGroup>
<NuGetAudit>true</NuGetAudit>
<NuGetAuditLevel>low</NuGetAuditLevel>
<NuGetAuditMode>all</NuGetAuditMode>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>
</Project>
After creating this, remove <TargetFramework>, <Nullable>, and <ImplicitUsings> from individual .csproj files to avoid duplication.
Optional: Separate Test Props
<!-- tests/Directory.Build.props -->
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
<PropertyGroup>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<!-- Use Microsoft.Testing.Platform v2 runner (requires Microsoft.NET.Test.Sdk 17.13+/18.x) -->
<UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner>
<!-- Tests don't need TreatWarningsAsErrors -->
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
</Project>
Step 4: Add Directory.Build.targets
Apply shared package references (SourceLink, analyzers) to all projects. Items go in .targets so they are imported after project evaluation.
<Project>
<ItemGroup>
<!-- SourceLink for debugger source navigation -->
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="all" />
</ItemGroup>
</Project>
The built-in Roslyn analyzers are already enabled by the AnalysisLevel and EnforceCodeStyleInBuild properties in Directory.Build.props (Step 3). For additional third-party analyzers, see [skill:dotnet-add-analyzers].
Step 5: Set Up Central Package Management
Create Directory.Packages.props at the repo root.
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<ItemGroup>
<!-- Framework packages -->
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<!-- Test packages -->
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="xunit.v3" Version="3.2.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="coverlet.collector" Version="8.0.0" />
</ItemGroup>
</Project>
After creating this, remove Version attributes from all <PackageReference> elements in .csproj files.
Step 6: Add .editorconfig
Create at the repo root. See [skill:dotnet-project-structure] for the full recommended config.
Minimal starter:
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
dotnet_style_require_accessibility_modifiers = always:warning
dotnet_sort_system_directives_first = true
csharp_using_directive_placement = outside_namespace:warning
Step 7: Add nuget.config
Configure package sources with supply-chain security:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>
Step 8: Add .gitignore
dotnet new gitignore
This generates the standard .NET .gitignore covering bin/, obj/, *.user, etc.
Step 9: Clean Up Generated Projects
After scaffolding, apply the shared configuration:
- Remove duplicated properties from individual
.csprojfiles (TargetFramework, Nullable, ImplicitUsings — these are in Directory.Build.props) - Remove Version attributes from PackageReference elements (managed by CPM)
- Delete template-generated Class1.cs from class libraries
- Set file-scoped namespaces in all generated
.csfiles
Cleaned csproj Example
Before (template-generated):
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>
After (with shared props and CPM):
<Project Sdk="Microsoft.NET.Sdk">
</Project>
For web projects that need Microsoft.NET.Sdk.Web, the csproj still specifies the SDK but inherits everything else.
Step 10: Verify
Run these commands to verify the scaffolded project:
# Restore and verify lock files generated
dotnet restore
find . -name "packages.lock.json" -type f
# Build with all analyzers
dotnet build --no-restore
# Run tests
dotnet test --no-build
# Verify CPM is active (no Version attributes in project PackageReferences)
# Should only find versions in Directory.Packages.props, not in csproj files
find . -name "*.csproj" -exec grep -l 'Version=' {} \; # expect no output
Final Structure
MyApp/
├── .editorconfig
├── .gitignore
├── global.json
├── nuget.config
├── MyApp.slnx
├── Directory.Build.props
├── Directory.Build.targets
├── Directory.Packages.props
├── src/
│ ├── MyApp.Core/
│ │ └── MyApp.Core.csproj
│ └── MyApp.Api/
│ ├── MyApp.Api.csproj
│ ├── Program.cs
│ └── appsettings.json
└── tests/
└── MyApp.UnitTests/
├── MyApp.UnitTests.csproj
└── SampleTest.cs
References
More from novotnyllc/dotnet-artisan
dotnet-csharp
Baseline C# skill loaded for every .NET code path. Guides language patterns (records, pattern matching, primary constructors, C# 8-15), coding standards, async/await, DI, LINQ, serialization, domain modeling, concurrency, Roslyn analyzers, globalization, native interop (P/Invoke, LibraryImport, ComWrappers), WASM interop (JSImport/JSExport), and type design. Spans 25 topics. Do not use for ASP.NET endpoint architecture, UI framework patterns, or CI/CD guidance.
129dotnet-ui
Builds .NET UI apps across Blazor (Server, WASM, Hybrid, Auto), MAUI (XAML, MVVM, Shell, Native AOT), Uno Platform (MVUX, Extensions, Toolkit), WPF (.NET 8+, Fluent theme), WinUI 3 (Windows App SDK, MSIX, Mica/Acrylic, adaptive layout), and WinForms (high-DPI, dark mode) with JS interop, accessibility (SemanticProperties, ARIA), localization (.resx, RTL), platform bindings (Java.Interop, ObjCRuntime), and framework selection. Spans 20 topic areas. Do not use for backend API design or CI/CD pipelines.
99dotnet-api
Builds ASP.NET Core APIs, EF Core data access, gRPC, SignalR, and backend services with middleware, security (OAuth, JWT, OWASP), resilience, messaging, OpenAPI, .NET Aspire, Semantic Kernel, HybridCache, YARP reverse proxy, output caching, Office documents (Excel, Word, PowerPoint), PDF, and architecture patterns. Spans 32 topic areas. Do not use for UI rendering patterns or CI/CD pipeline authoring.
90dotnet-testing
Defines .NET test strategy and implementation patterns across xUnit v3 (Facts, Theories, fixtures, IAsyncLifetime), integration testing (WebApplicationFactory, Testcontainers), Aspire testing (DistributedApplicationTestingBuilder), snapshot testing (Verify, scrubbing), Playwright E2E browser automation, BenchmarkDotNet microbenchmarks, code coverage (Coverlet), mutation testing (Stryker.NET), UI testing (page objects, selectors), and AOT WASM test compilation. Spans 13 topic areas. Do not use for production API architecture or CI workflow authoring.
86dotnet-advisor
Routes .NET/C# requests to the correct domain skill and loads coding standards as baseline for all code paths. Determines whether the task needs API, UI, testing, devops, tooling, or debugging guidance based on prompt analysis and project signals, then invokes skills in the right order. Always invoked after [skill:using-dotnet] detects .NET intent. Do not use for deep API, UI, testing, devops, tooling, or debugging implementation guidance.
60dotnet-debugging
Debugs Windows and Linux/macOS applications (native, .NET/CLR, mixed-mode) with WinDbg MCP (crash dumps, !analyze, !syncblk, !dlk, !runaway, !dumpheap, !gcroot, BSOD), dotnet-dump, lldb with SOS, createdump, and container diagnostics (Docker, Kubernetes). Hang/deadlock diagnosis, high CPU triage, memory leak investigation, kernel debugging, and dotnet-monitor for production. Spans 17 topic areas. Do not use for routine .NET SDK profiling, benchmark design, or CI test debugging.
57