Claude
Skill
dotnet-project-setup
Create or reorganize .NET solutions with clean project boundaries, repeatable SDK settings, and a maintainable baseline for libraries, apps, tests, CI, and local development.
Virus-scanned
Reviewed automatically before listing.
Download
postpartum-genushyacinthus29-dotnet-skills-skills_dotnet-project-setup-bfa4ebd.zip · 8 KB
Install
skills CLI
npx skills add https://github.com/Postpartum-genushyacinthus29/dotnet-skills/tree/main/skills/dotnet-project-setup
Claude Code
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install postpartum-genushyacinthus29-dotnet-skills@llmmart
Git
git clone https://github.com/Postpartum-genushyacinthus29/dotnet-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole postpartum-genushyacinthus29/dotnet-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
.NET Project Setup
Trigger On
- creating a new .NET solution or restructuring an existing one
- setting up
Directory.Build.props, shared package management, or repo-wide defaults - defining project layout for apps, libraries, and test projects
Workflow
- Start from the app model and deployment target, then choose the smallest correct SDK and target framework set.
- Use solution folders and project names that reflect bounded contexts or product areas, not temporary implementation details.
- Centralize shared build settings, analyzer rules, nullable context, and package versions where it reduces duplication without hiding important differences.
- Create test projects and CI hooks early so new projects do not drift into unverified templates.
- Prefer project references and composition over circular dependencies or utility dumping grounds.
- Document the local build, test, and run path in repo docs or
AGENTS.mdwhen the workflow is not obvious.
Deliver
- a coherent solution structure
- shared build defaults that are easy to reason about
- starter quality and testing hooks for future work
Validate
- projects have explicit responsibility boundaries
- shared MSBuild settings do not accidentally override platform-specific needs
- a new contributor can build and test the repo without guessing
References
- patterns.md: solution layout conventions,
Directory.Build.props,Directory.Build.targets, Central Package Management,global.json,nuget.config, analyzers, multi-targeting, and source link - templates.md:
dotnet newtemplates for console apps, class libraries, ASP.NET Core APIs, worker services, Blazor, test projects, .NET Aspire, and gRPC services
Files (dotnet-skills)
-
references
-
patterns.md 11 KB
# Project Structure Patterns ## Solution Layout ### Recommended Directory Structure ```text <repo-root>/ ├── .config/ │ └── dotnet-tools.json ├── src/ │ ├── <ProductName>.Core/ │ ├── <ProductName>.Api/ │ └── <ProductName>.Web/ ├── tests/ │ ├── <ProductName>.Core.Tests/ │ └── <ProductName>.Api.Tests/ ├── samples/ │ └── <ProductName>.Sample/ ├── docs/ ├── Directory.Build.props ├── Directory.Build.targets ├── Directory.Packages.props ├── global.json ├── nuget.config ├── <SolutionName>.sln └── README.md ``` ### Project Naming Conventions | Project Type | Pattern | Example | |--------------|---------|---------| | Core library | `<ProductName>.Core` | `Contoso.Orders.Core` | | Domain layer | `<ProductName>.Domain` | `Contoso.Orders.Domain` | | Application layer | `<ProductName>.Application` | `Contoso.Orders.Application` | | Infrastructure | `<ProductName>.Infrastructure` | `Contoso.Orders.Infrastructure` | | Web API | `<ProductName>.Api` | `Contoso.Orders.Api` | | Web frontend | `<ProductName>.Web` | `Contoso.Orders.Web` | | Worker service | `<ProductName>.Worker` | `Contoso.Orders.Worker` | | Unit tests | `<ProjectName>.Tests` | `Contoso.Orders.Core.Tests` | | Integration tests | `<ProjectName>.IntegrationTests` | `Contoso.Orders.Api.IntegrationTests` | --- ## Directory.Build.props `Directory.Build.props` is automatically imported by MSBuild for all projects in its directory and subdirectories. ### Basic Template ```xml <Project> <PropertyGroup> <TargetFramework>net9.0</TargetFramework> <LangVersion>latest</LangVersion> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <TreatWarningsAsErrors>true</TreatWarningsAsErrors> <WarningsAsErrors /> <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild> <EnableNETAnalyzers>true</EnableNETAnalyzers> <AnalysisLevel>latest-recommended</AnalysisLevel> </PropertyGroup> <!-- Package metadata for libraries --> <PropertyGroup> <Authors>Your Name or Organization</Authors> <Company>Your Company</Company> <Copyright>Copyright (c) $(Company) $([System.DateTime]::Now.Year)</Copyright> <RepositoryUrl>https://github.com/your-org/your-repo</RepositoryUrl> <RepositoryType>git</RepositoryType> </PropertyGroup> <!-- Deterministic builds for CI --> <PropertyGroup Condition="'$(CI)' == 'true'"> <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild> <Deterministic>true</Deterministic> </PropertyGroup> </Project> ``` ### Conditional Properties by Project Type ```xml <Project> <!-- Shared defaults --> <PropertyGroup> <TargetFramework>net9.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> </PropertyGroup> <!-- Test project defaults --> <PropertyGroup Condition="$(MSBuildProjectName.EndsWith('.Tests'))"> <IsPackable>false</IsPackable> <IsTestProject>true</IsTestProject> </PropertyGroup> <!-- Library defaults --> <PropertyGroup Condition="!$(MSBuildProjectName.EndsWith('.Tests')) AND !$(MSBuildProjectName.EndsWith('.Api')) AND !$(MSBuildProjectName.EndsWith('.Web'))"> <GenerateDocumentationFile>true</GenerateDocumentationFile> </PropertyGroup> </Project> ``` ### Nested Directory.Build.props Child directories can extend the parent by importing it explicitly: ```xml <!-- tests/Directory.Build.props --> <Project> <Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" /> <PropertyGroup> <IsPackable>false</IsPackable> <IsTestProject>true</IsTestProject> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.NET.Test.Sdk" /> <PackageReference Include="xunit" /> <PackageReference Include="xunit.runner.visualstudio" /> <PackageReference Include="coverlet.collector" /> </ItemGroup> </Project> ``` --- ## Directory.Build.targets Use `Directory.Build.targets` for logic that runs after the project file is fully evaluated. ```xml <Project> <!-- Run after project evaluation --> <Target Name="PrintBuildInfo" BeforeTargets="Build"> <Message Importance="High" Text="Building $(MSBuildProjectName) for $(TargetFramework)" /> </Target> <!-- Enforce test naming convention --> <Target Name="ValidateTestProjectNaming" BeforeTargets="Build" Condition="'$(IsTestProject)' == 'true'"> <Error Condition="!$(MSBuildProjectName.EndsWith('.Tests')) AND !$(MSBuildProjectName.EndsWith('.IntegrationTests'))" Text="Test projects must end with .Tests or .IntegrationTests" /> </Target> </Project> ``` --- ## Central Package Management (CPM) Central Package Management consolidates package versions into a single `Directory.Packages.props` file. ### Enabling CPM Add to `Directory.Packages.props` at the repository root: ```xml <Project> <PropertyGroup> <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> <CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled> </PropertyGroup> <ItemGroup> <!-- Runtime packages --> <PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" /> <PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.0" /> <PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.0" /> <PackageVersion Include="System.Text.Json" Version="9.0.0" /> <!-- Entity Framework Core --> <PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.0" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0" /> <!-- ASP.NET Core extras --> <PackageVersion Include="Swashbuckle.AspNetCore" Version="6.6.2" /> <!-- Testing --> <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" /> <PackageVersion Include="xunit" Version="2.9.2" /> <PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" /> <PackageVersion Include="Moq" Version="4.20.72" /> <PackageVersion Include="FluentAssertions" Version="6.12.1" /> <PackageVersion Include="coverlet.collector" Version="6.0.2" /> <!-- Analyzers --> <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" /> <PackageVersion Include="Roslynator.Analyzers" Version="4.12.6" /> </ItemGroup> </Project> ``` ### Project File References with CPM When CPM is enabled, project files reference packages without versions: ```xml <ItemGroup> <PackageReference Include="Microsoft.Extensions.Hosting" /> <PackageReference Include="Microsoft.Extensions.Logging" /> </ItemGroup> ``` ### Version Overrides Use `VersionOverride` sparingly when a project requires a different version: ```xml <ItemGroup> <PackageReference Include="Newtonsoft.Json" VersionOverride="13.0.3" /> </ItemGroup> ``` --- ## global.json Pin the SDK version for reproducible builds: ```json { "sdk": { "version": "9.0.100", "rollForward": "latestFeature", "allowPrerelease": false } } ``` ### Roll-Forward Policies | Value | Behavior | |-------|----------| | `patch` | Use specified or highest installed patch | | `feature` | Use specified or highest installed feature band | | `minor` | Use specified or highest installed minor | | `major` | Use highest installed SDK | | `latestPatch` | Use highest installed patch | | `latestFeature` | Use highest installed feature band | | `latestMinor` | Use highest installed minor | | `latestMajor` | Use highest installed SDK | | `disable` | Exact match only | --- ## nuget.config Configure package sources and credentials: ```xml <?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources> <clear /> <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> <!-- Private feeds --> <add key="github" value="https://nuget.pkg.github.com/your-org/index.json" /> </packageSources> <packageSourceMapping> <packageSource key="nuget.org"> <package pattern="*" /> </packageSource> <packageSource key="github"> <package pattern="YourOrg.*" /> </packageSource> </packageSourceMapping> <!-- CI credentials via environment variables --> <packageSourceCredentials> <github> <add key="Username" value="%NUGET_USERNAME%" /> <add key="ClearTextPassword" value="%NUGET_TOKEN%" /> </github> </packageSourceCredentials> </configuration> ``` --- ## Analyzers and Code Style ### .editorconfig Basics ```ini root = true [*] charset = utf-8 end_of_line = lf indent_style = space indent_size = 4 insert_final_newline = true trim_trailing_whitespace = true [*.{cs,vb}] dotnet_sort_system_directives_first = true dotnet_separate_import_directive_groups = false [*.cs] csharp_style_namespace_declarations = file_scoped:warning csharp_style_var_for_built_in_types = true:suggestion csharp_style_var_when_type_is_apparent = true:suggestion csharp_style_var_elsewhere = true:suggestion csharp_prefer_braces = true:warning csharp_style_expression_bodied_methods = when_on_single_line:suggestion csharp_style_expression_bodied_constructors = false:suggestion # IDE diagnostics dotnet_diagnostic.IDE0005.severity = warning dotnet_diagnostic.IDE0055.severity = warning [*.{json,yml,yaml}] indent_size = 2 ``` ### Analyzer Packages in Directory.Build.props ```xml <ItemGroup> <PackageReference Include="StyleCop.Analyzers" PrivateAssets="all" /> <PackageReference Include="Roslynator.Analyzers" PrivateAssets="all" /> <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" PrivateAssets="all" /> </ItemGroup> ``` --- ## Multi-Targeting ### Single Project Multi-Targeting ```xml <PropertyGroup> <TargetFrameworks>net9.0;net8.0;netstandard2.0</TargetFrameworks> </PropertyGroup> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <PackageReference Include="System.Text.Json" /> </ItemGroup> ``` ### Conditional Compilation ```csharp #if NET9_0_OR_GREATER // .NET 9+ specific code ArgumentNullException.ThrowIfNull(value); #else // Fallback for older frameworks if (value is null) throw new ArgumentNullException(nameof(value)); #endif ``` --- ## Source Link and Deterministic Builds Enable source link for debugger integration: ```xml <PropertyGroup> <PublishRepositoryUrl>true</PublishRepositoryUrl> <EmbedUntrackedSources>true</EmbedUntrackedSources> <IncludeSymbols>true</IncludeSymbols> <SymbolPackageFormat>snupkg</SymbolPackageFormat> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="all" /> </ItemGroup> ``` --- ## Solution Filters Create `.slnf` files for partial solution loading: ```json { "solution": { "path": "MyProduct.sln", "projects": [ "src\\MyProduct.Core\\MyProduct.Core.csproj", "src\\MyProduct.Api\\MyProduct.Api.csproj", "tests\\MyProduct.Core.Tests\\MyProduct.Core.Tests.csproj" ] } } ``` Open with: `dotnet sln open MyProduct.Api.slnf` -
templates.md 12.9 KB
# Common Project Templates ## Overview .NET provides project templates via `dotnet new`. This reference covers common templates and their typical use cases. --- ## Listing Available Templates ```bash # List all installed templates dotnet new list # Search for templates dotnet new search webapi # Install a template pack dotnet new install Microsoft.AspNetCore.SpaTemplates ``` --- ## Console Applications ### Basic Console App ```bash dotnet new console -n MyApp -o src/MyApp ``` **Generated structure:** ```text src/MyApp/ ├── MyApp.csproj └── Program.cs ``` **Typical `Program.cs`:** ```csharp // Minimal hosting for console apps with DI using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; var builder = Host.CreateApplicationBuilder(args); builder.Services.AddSingleton<MyService>(); using var host = builder.Build(); await host.RunAsync(); ``` --- ## Class Libraries ### Standard Library ```bash dotnet new classlib -n MyProduct.Core -o src/MyProduct.Core ``` ### Library with Multi-Targeting **Modify the generated `.csproj`:** ```xml <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFrameworks>net9.0;net8.0;netstandard2.0</TargetFrameworks> <GenerateDocumentationFile>true</GenerateDocumentationFile> <PackageId>MyCompany.MyProduct.Core</PackageId> <Description>Core library for MyProduct</Description> </PropertyGroup> </Project> ``` --- ## ASP.NET Core Web API ### Minimal API ```bash dotnet new webapi -n MyProduct.Api -o src/MyProduct.Api --use-minimal-apis ``` **Typical structure:** ```text src/MyProduct.Api/ ├── MyProduct.Api.csproj ├── Program.cs ├── Properties/ │ └── launchSettings.json ├── appsettings.json └── appsettings.Development.json ``` **Minimal API `Program.cs` example:** ```csharp var builder = WebApplication.CreateBuilder(args); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.MapGet("/api/health", () => Results.Ok(new { Status = "Healthy" })) .WithName("HealthCheck") .WithOpenApi(); app.Run(); ``` ### Controller-Based API ```bash dotnet new webapi -n MyProduct.Api -o src/MyProduct.Api --use-controllers ``` **Controller template:** ```csharp using Microsoft.AspNetCore.Mvc; namespace MyProduct.Api.Controllers; [ApiController] [Route("api/[controller]")] public class ItemsController : ControllerBase { private readonly IItemService _itemService; public ItemsController(IItemService itemService) { _itemService = itemService; } [HttpGet] public async Task<ActionResult<IEnumerable<ItemDto>>> GetAll(CancellationToken ct) { var items = await _itemService.GetAllAsync(ct); return Ok(items); } [HttpGet("{id:guid}")] public async Task<ActionResult<ItemDto>> GetById(Guid id, CancellationToken ct) { var item = await _itemService.GetByIdAsync(id, ct); return item is null ? NotFound() : Ok(item); } [HttpPost] public async Task<ActionResult<ItemDto>> Create(CreateItemRequest request, CancellationToken ct) { var item = await _itemService.CreateAsync(request, ct); return CreatedAtAction(nameof(GetById), new { id = item.Id }, item); } } ``` --- ## Worker Services ### Background Service ```bash dotnet new worker -n MyProduct.Worker -o src/MyProduct.Worker ``` **Worker template:** ```csharp namespace MyProduct.Worker; public class Worker : BackgroundService { private readonly ILogger<Worker> _logger; private readonly IServiceScopeFactory _scopeFactory; public Worker(ILogger<Worker> logger, IServiceScopeFactory scopeFactory) { _logger = logger; _scopeFactory = scopeFactory; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { _logger.LogInformation("Worker running at: {Time}", DateTimeOffset.Now); using var scope = _scopeFactory.CreateScope(); var service = scope.ServiceProvider.GetRequiredService<IMyService>(); await service.ProcessAsync(stoppingToken); await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken); } } } ``` --- ## Web Applications ### Blazor Server ```bash dotnet new blazor -n MyProduct.Web -o src/MyProduct.Web --interactivity Server ``` ### Blazor WebAssembly ```bash dotnet new blazor -n MyProduct.Web -o src/MyProduct.Web --interactivity WebAssembly ``` ### Razor Pages ```bash dotnet new webapp -n MyProduct.Web -o src/MyProduct.Web ``` --- ## Test Projects ### xUnit Test Project ```bash dotnet new xunit -n MyProduct.Core.Tests -o tests/MyProduct.Core.Tests ``` **Test project `.csproj`:** ```xml <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net9.0</TargetFramework> <IsPackable>false</IsPackable> <IsTestProject>true</IsTestProject> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.NET.Test.Sdk" /> <PackageReference Include="xunit" /> <PackageReference Include="xunit.runner.visualstudio" /> <PackageReference Include="coverlet.collector" /> <PackageReference Include="FluentAssertions" /> <PackageReference Include="Moq" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\src\MyProduct.Core\MyProduct.Core.csproj" /> </ItemGroup> </Project> ``` **Test class template:** ```csharp using FluentAssertions; using Moq; using Xunit; namespace MyProduct.Core.Tests; public class ItemServiceTests { private readonly Mock<IItemRepository> _repositoryMock; private readonly ItemService _sut; public ItemServiceTests() { _repositoryMock = new Mock<IItemRepository>(); _sut = new ItemService(_repositoryMock.Object); } [Fact] public async Task GetByIdAsync_WhenItemExists_ReturnsItem() { // Arrange var itemId = Guid.NewGuid(); var expected = new Item { Id = itemId, Name = "Test" }; _repositoryMock .Setup(r => r.GetByIdAsync(itemId, It.IsAny<CancellationToken>())) .ReturnsAsync(expected); // Act var result = await _sut.GetByIdAsync(itemId, CancellationToken.None); // Assert result.Should().BeEquivalentTo(expected); } [Theory] [InlineData("")] [InlineData(" ")] [InlineData(null)] public async Task CreateAsync_WhenNameInvalid_ThrowsArgumentException(string? name) { // Arrange var request = new CreateItemRequest { Name = name! }; // Act var act = () => _sut.CreateAsync(request, CancellationToken.None); // Assert await act.Should().ThrowAsync<ArgumentException>(); } } ``` ### NUnit Test Project ```bash dotnet new nunit -n MyProduct.Core.Tests -o tests/MyProduct.Core.Tests ``` ### MSTest Test Project ```bash dotnet new mstest -n MyProduct.Core.Tests -o tests/MyProduct.Core.Tests ``` ### Integration Test Project ```csharp // WebApplicationFactory-based integration tests using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; using Xunit; namespace MyProduct.Api.IntegrationTests; public class ItemsEndpointTests : IClassFixture<WebApplicationFactory<Program>> { private readonly HttpClient _client; public ItemsEndpointTests(WebApplicationFactory<Program> factory) { _client = factory .WithWebHostBuilder(builder => { builder.ConfigureServices(services => { // Replace services for testing services.AddSingleton<IItemRepository, InMemoryItemRepository>(); }); }) .CreateClient(); } [Fact] public async Task GetAll_ReturnsOkStatus() { // Act var response = await _client.GetAsync("/api/items"); // Assert response.EnsureSuccessStatusCode(); } } ``` --- ## Solution Setup Commands ### Create Solution and Projects ```bash # Create solution dotnet new sln -n MyProduct # Create projects dotnet new classlib -n MyProduct.Core -o src/MyProduct.Core dotnet new classlib -n MyProduct.Infrastructure -o src/MyProduct.Infrastructure dotnet new webapi -n MyProduct.Api -o src/MyProduct.Api --use-minimal-apis dotnet new xunit -n MyProduct.Core.Tests -o tests/MyProduct.Core.Tests dotnet new xunit -n MyProduct.Api.IntegrationTests -o tests/MyProduct.Api.IntegrationTests # Add projects to solution dotnet sln add src/MyProduct.Core/MyProduct.Core.csproj dotnet sln add src/MyProduct.Infrastructure/MyProduct.Infrastructure.csproj dotnet sln add src/MyProduct.Api/MyProduct.Api.csproj dotnet sln add tests/MyProduct.Core.Tests/MyProduct.Core.Tests.csproj dotnet sln add tests/MyProduct.Api.IntegrationTests/MyProduct.Api.IntegrationTests.csproj # Add project references dotnet add src/MyProduct.Infrastructure/MyProduct.Infrastructure.csproj reference src/MyProduct.Core/MyProduct.Core.csproj dotnet add src/MyProduct.Api/MyProduct.Api.csproj reference src/MyProduct.Core/MyProduct.Core.csproj dotnet add src/MyProduct.Api/MyProduct.Api.csproj reference src/MyProduct.Infrastructure/MyProduct.Infrastructure.csproj dotnet add tests/MyProduct.Core.Tests/MyProduct.Core.Tests.csproj reference src/MyProduct.Core/MyProduct.Core.csproj dotnet add tests/MyProduct.Api.IntegrationTests/MyProduct.Api.IntegrationTests.csproj reference src/MyProduct.Api/MyProduct.Api.csproj ``` --- ## .NET Aspire Application ### Aspire App Host ```bash dotnet new aspire -n MyProduct ``` **Creates:** ```text MyProduct/ ├── MyProduct.AppHost/ │ ├── MyProduct.AppHost.csproj │ └── Program.cs ├── MyProduct.ServiceDefaults/ │ ├── MyProduct.ServiceDefaults.csproj │ └── Extensions.cs └── MyProduct.sln ``` **AppHost `Program.cs`:** ```csharp var builder = DistributedApplication.CreateBuilder(args); var cache = builder.AddRedis("cache"); var postgres = builder.AddPostgres("postgres") .AddDatabase("ordersdb"); var api = builder.AddProject<Projects.MyProduct_Api>("api") .WithReference(cache) .WithReference(postgres); builder.AddProject<Projects.MyProduct_Web>("web") .WithReference(api); builder.Build().Run(); ``` ### Add Service to Existing Aspire Solution ```bash # Add a new API project dotnet new webapi -n MyProduct.OrdersApi -o src/MyProduct.OrdersApi dotnet sln add src/MyProduct.OrdersApi/MyProduct.OrdersApi.csproj # Reference ServiceDefaults dotnet add src/MyProduct.OrdersApi/MyProduct.OrdersApi.csproj reference src/MyProduct.ServiceDefaults/MyProduct.ServiceDefaults.csproj ``` --- ## gRPC Services ```bash dotnet new grpc -n MyProduct.GrpcService -o src/MyProduct.GrpcService ``` **Proto file template:** ```protobuf syntax = "proto3"; option csharp_namespace = "MyProduct.GrpcService"; package orders; service OrderService { rpc GetOrder (GetOrderRequest) returns (OrderResponse); rpc CreateOrder (CreateOrderRequest) returns (OrderResponse); rpc ListOrders (ListOrdersRequest) returns (stream OrderResponse); } message GetOrderRequest { string order_id = 1; } message CreateOrderRequest { string customer_id = 1; repeated OrderItem items = 2; } message OrderItem { string product_id = 1; int32 quantity = 2; } message OrderResponse { string order_id = 1; string customer_id = 2; repeated OrderItem items = 3; string status = 4; } message ListOrdersRequest { string customer_id = 1; } ``` --- ## Tool Manifest ### Create Tool Manifest ```bash dotnet new tool-manifest ``` **Creates `.config/dotnet-tools.json`:** ```json { "version": 1, "isRoot": true, "tools": {} } ``` ### Install Local Tools ```bash dotnet tool install dotnet-ef dotnet tool install dotnet-format dotnet tool install dotnet-reportgenerator-globaltool ``` **Updated manifest:** ```json { "version": 1, "isRoot": true, "tools": { "dotnet-ef": { "version": "9.0.0", "commands": ["dotnet-ef"] }, "dotnet-format": { "version": "5.1.250801", "commands": ["dotnet-format"] }, "dotnet-reportgenerator-globaltool": { "version": "5.3.10", "commands": ["reportgenerator"] } } } ``` ### Restore Tools ```bash dotnet tool restore ``` --- ## Quick Reference: Common Template Options | Template | Command | Key Options | |----------|---------|-------------| | Console | `dotnet new console` | `--use-program-main` | | Class Library | `dotnet new classlib` | `--framework` | | Web API | `dotnet new webapi` | `--use-controllers`, `--use-minimal-apis`, `--auth` | | Blazor | `dotnet new blazor` | `--interactivity`, `--empty` | | Worker | `dotnet new worker` | `--framework` | | xUnit | `dotnet new xunit` | `--framework` | | Solution | `dotnet new sln` | - | | gitignore | `dotnet new gitignore` | - | | editorconfig | `dotnet new editorconfig` | - | | global.json | `dotnet new globaljson` | `--sdk-version`, `--roll-forward` |
-
-
SKILL.md 2.1 KB
--- name: dotnet-project-setup version: "1.0.0" category: "Core" description: "Create or reorganize .NET solutions with clean project boundaries, repeatable SDK settings, and a maintainable baseline for libraries, apps, tests, CI, and local development." compatibility: "Best for new repositories or structural refactors of existing .NET solutions." --- # .NET Project Setup ## Trigger On - creating a new .NET solution or restructuring an existing one - setting up `Directory.Build.props`, shared package management, or repo-wide defaults - defining project layout for apps, libraries, and test projects ## Workflow 1. Start from the app model and deployment target, then choose the smallest correct SDK and target framework set. 2. Use solution folders and project names that reflect bounded contexts or product areas, not temporary implementation details. 3. Centralize shared build settings, analyzer rules, nullable context, and package versions where it reduces duplication without hiding important differences. 4. Create test projects and CI hooks early so new projects do not drift into unverified templates. 5. Prefer project references and composition over circular dependencies or utility dumping grounds. 6. Document the local build, test, and run path in repo docs or `AGENTS.md` when the workflow is not obvious. ## Deliver - a coherent solution structure - shared build defaults that are easy to reason about - starter quality and testing hooks for future work ## Validate - projects have explicit responsibility boundaries - shared MSBuild settings do not accidentally override platform-specific needs - a new contributor can build and test the repo without guessing ## References - [patterns.md](references/patterns.md): solution layout conventions, `Directory.Build.props`, `Directory.Build.targets`, Central Package Management, `global.json`, `nuget.config`, analyzers, multi-targeting, and source link - [templates.md](references/templates.md): `dotnet new` templates for console apps, class libraries, ASP.NET Core APIs, worker services, Blazor, test projects, .NET Aspire, and gRPC services
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.