chore(backend): install AI agent skills for the .NET stack

Run autoskills in backend/: detects .NET/C#/ASP.NET Core/Minimal API and
installs 13 skills for universal (.agents) and claude-code (.claude) agents,
with backend/skills-lock.json. Adds ASP.NET Core, Minimal API + OpenAPI,
file-upload and containerization skills on top of the prior C#/.NET set.
Removes the stale root skills from the old layout.
This commit is contained in:
2026-07-05 03:48:31 +02:00
parent f2933caa90
commit 3b98a05c3b
43 changed files with 1808 additions and 0 deletions
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@@ -0,0 +1,61 @@
---
name: aspnet-core
description: Build, review, refactor, or architect ASP.NET Core web applications using current official guidance for .NET web development. Use when working on Blazor Web Apps, Razor Pages, MVC, Minimal APIs, controller-based Web APIs, SignalR, gRPC, middleware, dependency injection, configuration, authentication, authorization, testing, performance, deployment, or ASP.NET Core upgrades.
---
# ASP.NET Core
## Overview
Choose the right ASP.NET Core application model, compose the host and request pipeline correctly, and implement features in the framework style Microsoft documents today.
Load the smallest set of references that fits the task. Do not load every reference by default.
## Workflow
1. Confirm the target framework, SDK, and current app model.
2. Open [references/stack-selection.md](references/stack-selection.md) first for new apps or major refactors.
3. Open [references/program-and-pipeline.md](references/program-and-pipeline.md) next for `Program.cs`, DI, configuration, middleware, routing, logging, and static assets.
4. Open exactly one primary app-model reference:
- [references/ui-blazor.md](references/ui-blazor.md)
- [references/ui-razor-pages.md](references/ui-razor-pages.md)
- [references/ui-mvc.md](references/ui-mvc.md)
- [references/apis-minimal-and-controllers.md](references/apis-minimal-and-controllers.md)
5. Add cross-cutting references only as needed:
- [references/data-state-and-services.md](references/data-state-and-services.md)
- [references/security-and-identity.md](references/security-and-identity.md)
- [references/realtime-grpc-and-background-work.md](references/realtime-grpc-and-background-work.md)
- [references/testing-performance-and-operations.md](references/testing-performance-and-operations.md)
6. Open [references/versioning-and-upgrades.md](references/versioning-and-upgrades.md) before introducing new platform APIs into an older solution or when migrating between major versions.
7. Use [references/source-map.md](references/source-map.md) when you need the Microsoft Learn section that corresponds to a task not already covered by the focused references.
## Default Operating Assumptions
- Prefer the latest stable ASP.NET Core and .NET unless the repository or user request pins an older target.
- As of March 2026, prefer .NET 10 / ASP.NET Core 10 for new production work. Treat ASP.NET Core 11 as preview unless the user explicitly asks for preview features.
- Prefer `WebApplicationBuilder` and `WebApplication`. Avoid older `Startup` and `WebHost` patterns unless the codebase already uses them or the task is migration.
- Prefer built-in DI, options/configuration, logging, ProblemDetails, OpenAPI, health checks, rate limiting, output caching, and Identity before adding third-party infrastructure.
- Keep feature slices cohesive so the page, component, endpoint, controller, validation, service, data access, and tests are easy to trace.
- Respect the existing app model. Do not rewrite Razor Pages to MVC or controllers to Minimal APIs without a clear reason.
## Reference Guide
- [references/_sections.md](references/_sections.md): Quick index and reading order.
- [references/stack-selection.md](references/stack-selection.md): Choose the right ASP.NET Core application model and template.
- [references/program-and-pipeline.md](references/program-and-pipeline.md): Structure `Program.cs`, services, middleware, routing, configuration, logging, and static assets.
- [references/ui-blazor.md](references/ui-blazor.md): Build Blazor Web Apps, choose render modes, and use components, forms, and JS interop correctly.
- [references/ui-razor-pages.md](references/ui-razor-pages.md): Build page-focused server-rendered apps with handlers, model binding, and conventions.
- [references/ui-mvc.md](references/ui-mvc.md): Build controller/view applications with clear separation of concerns.
- [references/apis-minimal-and-controllers.md](references/apis-minimal-and-controllers.md): Build HTTP APIs with Minimal APIs or controllers, including validation and response patterns.
- [references/data-state-and-services.md](references/data-state-and-services.md): Use EF Core, `DbContext`, options, `IHttpClientFactory`, session, temp data, and app state responsibly.
- [references/security-and-identity.md](references/security-and-identity.md): Apply authentication, authorization, Identity, secrets, data protection, CORS, CSRF, and HTTPS guidance.
- [references/realtime-grpc-and-background-work.md](references/realtime-grpc-and-background-work.md): Use SignalR, gRPC, and hosted services.
- [references/testing-performance-and-operations.md](references/testing-performance-and-operations.md): Add integration tests, browser tests, caching, compression, health checks, rate limits, and deployment concerns.
- [references/versioning-and-upgrades.md](references/versioning-and-upgrades.md): Handle target frameworks, breaking changes, obsolete APIs, and migrations.
- [references/source-map.md](references/source-map.md): Map the official ASP.NET Core documentation tree to the references in this skill.
## Execution Notes
- When generating new code, start from the correct `dotnet new` template and keep the generated structure recognizable.
- When editing an existing solution, follow the solution's conventions first and use these references to avoid framework misuse or outdated patterns.
- When a task mentions "latest", verify the feature on Microsoft Learn or the ASP.NET Core docs repo before relying on memory.
@@ -0,0 +1,5 @@
interface:
display_name: "ASP.NET Core"
short_description: "[Windows only] Build and review ASP.NET Core web apps"
icon_large: "./assets/dotnet-logo.png"
default_prompt: "Create a new $aspnet-core website for me."
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

@@ -0,0 +1,40 @@
# Reference Sections
Use this file as the routing table for the rest of the skill.
## Start Here
- New app or major redesign: `stack-selection.md` -> `program-and-pipeline.md` -> one primary app-model reference -> `security-and-identity.md` -> `testing-performance-and-operations.md`
- Existing app feature work: primary app-model reference -> `program-and-pipeline.md` -> any needed cross-cutting references
- API-first work: `apis-minimal-and-controllers.md` -> `security-and-identity.md` -> `data-state-and-services.md` -> `testing-performance-and-operations.md`
- Authentication, authorization, or secrets: `security-and-identity.md`
- Realtime, streaming, or background processing: `realtime-grpc-and-background-work.md`
- Upgrade or migration work: `versioning-and-upgrades.md`
## Primary References
| File | Open when |
| --- | --- |
| `stack-selection.md` | Choose Blazor, Razor Pages, MVC, Minimal APIs, controllers, SignalR, or gRPC |
| `program-and-pipeline.md` | Structure `Program.cs`, services, configuration, middleware, routing, logging, static files, and app startup |
| `ui-blazor.md` | Build or review Blazor Web Apps and component-based UI |
| `ui-razor-pages.md` | Build or review page-focused server-rendered applications |
| `ui-mvc.md` | Build or review controller/view applications |
| `apis-minimal-and-controllers.md` | Build or review HTTP APIs |
## Cross-Cutting References
| File | Open when |
| --- | --- |
| `data-state-and-services.md` | Register services, use EF Core, handle options/configuration, or manage app state |
| `security-and-identity.md` | Add Identity, cookies, bearer auth, policies, CORS, CSRF, HTTPS, or secrets handling |
| `realtime-grpc-and-background-work.md` | Add SignalR, gRPC, streaming, or hosted services |
| `testing-performance-and-operations.md` | Add tests, caching, compression, health checks, rate limits, deployment, or proxy configuration |
| `versioning-and-upgrades.md` | Migrate across ASP.NET Core versions, avoid obsolete APIs, or target preview features deliberately |
| `source-map.md` | Map a task to the official ASP.NET Core documentation tree |
## Reading Strategy
- Open one app-model reference at a time unless the codebase genuinely mixes models.
- Prefer the framework's built-in abstractions first.
- Check `versioning-and-upgrades.md` before introducing APIs that might not exist in the repository's target framework.
@@ -0,0 +1,81 @@
# APIs: Minimal And Controllers
Primary docs:
- https://learn.microsoft.com/aspnet/core/fundamentals/minimal-apis
- https://learn.microsoft.com/aspnet/core/web-api/
- https://learn.microsoft.com/aspnet/core/fundamentals/error-handling-api
## First Decision
Choose between:
- Minimal APIs for focused, low-ceremony HTTP endpoints
- controller-based APIs for richer MVC conventions and attribute-driven behavior
Do not mix both styles in the same feature unless that split is genuinely useful.
## Minimal API Guidance
Prefer Minimal APIs when the surface is small to medium and you want concise endpoint definitions.
Good defaults:
- organize endpoints with route groups
- keep route handlers thin
- move business logic into services
- prefer `TypedResults` over untyped results
- use endpoint filters when cross-cutting behavior belongs at the endpoint layer
- use built-in validation support on supported target frameworks
Minimal API reminders:
- handler parameters can be bound from route, query, headers, body, form, or DI
- authorization can be applied with `RequireAuthorization`
- return `IResult` or `TypedResults` when response shape matters
- use OpenAPI support for discoverable contracts
On .NET 10, Minimal APIs support built-in validation with `AddValidation()`. Use that instead of inventing parallel validation infrastructure when the target framework supports it.
## Controller API Guidance
Prefer controllers when the API needs:
- `[ApiController]` behaviors
- attribute routing and conventions
- filters
- custom formatters
- mature controller organization in an existing codebase
Controller defaults:
- derive API controllers from `ControllerBase`
- annotate with `[ApiController]`
- use attribute routing
- return ProblemDetails-compatible failures
- let automatic model validation handle invalid requests unless there is a concrete override requirement
Key `[ApiController]` behaviors:
- attribute routing is required
- invalid model state automatically becomes HTTP 400
- binding source inference applies
- error responses use ProblemDetails patterns
## Shared API Practices
- Keep request and response DTOs separate from persistence models
- Use version-stable route and payload contracts
- Use `CreatedAt...` patterns for resource creation
- Prefer explicit status codes and typed results over implicit behavior
- Apply authorization at the endpoint or controller boundary, not only inside service methods
- Use `ProblemDetails` for errors instead of ad hoc JSON shapes
## Browser-Facing Notes
- Be careful with cookie-authenticated API endpoints and CORS
- For browser-based form or file upload endpoints, account for antiforgery requirements
- In ASP.NET Core 10, known API endpoints no longer use cookie-login redirects by default; rely on API-appropriate unauthorized responses instead
## Native AOT
Use `dotnet new webapiaot` only when native AOT is an explicit deployment requirement. Treat it as a constraint that affects library choice, reflection, JSON patterns, and compatibility.
@@ -0,0 +1,69 @@
# Data, State, And Services
Primary docs:
- https://learn.microsoft.com/aspnet/core/data/
- https://learn.microsoft.com/aspnet/core/fundamentals/dependency-injection
- https://learn.microsoft.com/aspnet/core/fundamentals/http-requests
- https://learn.microsoft.com/aspnet/core/fundamentals/app-state
## Dependency Injection Defaults
- Register infrastructure and business services in `Program.cs`
- Inject dependencies through constructors by default
- Keep scoped services request-bound
- Avoid resolving scoped services from singletons
- Use keyed or named patterns only when there is a real need for multiple implementations
## EF Core And DbContext
Use EF Core for common relational data access patterns unless the repository already uses another data layer.
Default guidance:
- register `DbContext` with `AddDbContext`
- treat `DbContext` as scoped
- keep queries and transactions in services, not UI code
- use migrations intentionally
- keep entities out of public API contracts and UI view models
Use `IDbContextFactory<TContext>` when the execution model is not request-scoped, such as:
- Blazor components with longer-lived scopes
- background services
- explicit factory-driven data work
## Options And Configuration
- Bind structured configuration into options classes
- validate options early when bad configuration should fail fast
- keep configuration access close to the service that owns it
- avoid scattering raw configuration keys across the codebase
## Outbound HTTP
Use `IHttpClientFactory` for outbound HTTP calls.
Prefer:
- named clients for distinct external systems
- typed clients for richer integrations
- delegating handlers for retries, headers, or telemetry concerns
Avoid manual `new HttpClient()` patterns scattered through request handlers.
## App State
Use the smallest state mechanism that fits:
- query string or route values for transparent request state
- form posts for user input
- TempData for short-lived redirect-friendly messages
- session only when necessary and with an understanding of its server-side and scaling implications
Do not treat session as the primary application data store.
## Caching And State Boundaries
- Keep cached data derivable from a durable source
- Separate cache shape from persistence shape when it improves safety or performance
- Revisit session, in-memory cache, and singleton state when the app scales to multiple instances
@@ -0,0 +1,103 @@
# Program And Pipeline
Primary docs:
- https://learn.microsoft.com/aspnet/core/fundamentals/
- https://learn.microsoft.com/aspnet/core/fundamentals/minimal-apis/webapplication
- https://learn.microsoft.com/aspnet/core/fundamentals/middleware/
- https://learn.microsoft.com/aspnet/core/fundamentals/configuration/
## Startup Shape
Prefer the modern hosting model:
1. Create `var builder = WebApplication.CreateBuilder(args);`
2. Register services on `builder.Services`
3. Build `var app = builder.Build();`
4. Configure middleware in the correct order
5. Map endpoints
6. Call `app.Run();`
Use older `Startup` patterns only when the repository already uses them or the task is migration.
## Service Registration
- Register framework services explicitly: Razor Pages, controllers, Razor components, authentication, authorization, health checks, rate limiting, response compression, output caching, EF Core, and `IHttpClientFactory`
- Keep business logic in services instead of controllers, page models, or route handlers
- Use constructor injection as the default
- Use options classes for structured configuration
- Choose lifetimes intentionally:
- singleton: stateless or shared infrastructure
- scoped: request-bound work such as `DbContext`
- transient: lightweight stateless services
## Configuration Defaults
`WebApplication.CreateBuilder` already loads configuration from common providers such as:
- `appsettings.json`
- environment-specific `appsettings.{Environment}.json`
- environment variables
- command-line arguments
For secrets:
- use Secret Manager in development
- use a secure external store in production
- do not commit secrets to source control
## Middleware Order
Middleware order is a frequent source of broken behavior. Favor this shape and adjust only with a concrete reason:
1. Forwarded headers if behind a proxy or load balancer
2. Exception handling and HSTS for non-development environments
3. HTTPS redirection
4. Static files
5. Routing when explicit routing middleware is needed
6. CORS when endpoints require it
7. Authentication
8. Authorization
9. Endpoint-specific middleware such as rate limiting or session as required
10. Endpoint mapping with `MapRazorPages`, `MapControllers`, `MapGet`, `MapHub`, or `MapGrpcService`
Important ordering rules:
- Call `UseAuthentication()` before `UseAuthorization()`
- Keep proxy/header processing before auth, redirects, and link generation
- Do not insert custom middleware randomly between auth and authorization without a reason
- In Minimal API apps, explicit `UseRouting()` is usually unnecessary unless you need to control order
## Routing And Endpoints
- Prefer endpoint routing everywhere
- Use route groups for larger Minimal API surfaces
- Keep MVC and API routes explicit and predictable
- Use areas only when the application is large enough to benefit from bounded sections
- Keep endpoint names stable when generating links or integrating with clients
## Error Handling
- Use centralized exception handling instead of scattered `try/catch` blocks for ordinary request failures
- Prefer ProblemDetails-style responses for APIs
- Keep the developer exception page limited to development
- Separate user-facing failures from internal exception details
## Logging And Diagnostics
- Use `ILogger<T>` from DI
- Log structured values, not concatenated strings
- Put correlation and request diagnostics in middleware or infrastructure, not business logic
- Enable HTTP logging only when the scenario warrants it and avoid leaking sensitive data
## Static Assets And Web Root
- Keep public assets in `wwwroot`
- Treat the web root as publicly readable content
- Prevent publishing local-only static content through project file rules when needed
- Use Razor Class Libraries for reusable UI assets across apps
## Architectural Defaults
- Keep `Program.cs` readable; extract feature registration to extension methods when it starts accumulating unrelated concerns
- Prefer vertical slices or feature folders over giant "Controllers", "Services", and "Repositories" buckets with weak boundaries
- Keep framework configuration close to the host and business logic out of it
@@ -0,0 +1,58 @@
# Realtime, gRPC, And Background Work
Primary docs:
- https://learn.microsoft.com/aspnet/core/signalr/introduction
- https://learn.microsoft.com/aspnet/core/grpc/
- https://learn.microsoft.com/aspnet/core/fundamentals/host/hosted-services
## SignalR
Use SignalR when the server must push updates to connected clients in near real time.
Good fits:
- chat
- dashboards
- notifications
- collaborative editing
- live status streams
Guidance:
- model the hub as a communication boundary, not the home of business logic
- use groups and user targeting deliberately
- authenticate connections when data is user-specific
- plan for scale-out if the app may run on multiple instances
Remember that Blazor interactive server rendering already relies on a real-time connection. Do not add a second realtime channel unless the feature truly needs one.
## gRPC
Use gRPC for efficient service-to-service communication, strongly typed contracts, and streaming over HTTP/2.
Prefer gRPC when:
- both ends are under your control
- performance and contract fidelity matter
- streaming is a first-class requirement
Guidance:
- keep `.proto` contracts versioned and stable
- generate client and server types from contracts
- keep auth, logging, and DI integrated with the host
- account for browser interoperability differences before choosing gRPC for public browser clients
## Background Work
Use `IHostedService` or `BackgroundService` for in-process background tasks tied to the application host.
Defaults:
- keep background services small and observable
- create scopes for scoped dependencies
- do not capture scoped services directly in singleton hosted services
- respect cancellation tokens
- avoid long blocking startup paths
If the work is durable, high-volume, or business-critical, consider whether it belongs in an out-of-process queue or worker instead of only inside the web host.
@@ -0,0 +1,75 @@
# Security And Identity
Primary docs:
- https://learn.microsoft.com/aspnet/core/security/
- https://learn.microsoft.com/aspnet/core/security/authentication/identity
- https://learn.microsoft.com/aspnet/core/security/authorization/introduction
## Security Defaults
- Use the most secure authentication flow available
- Keep secrets out of source code and plain configuration files
- Use Secret Manager in development
- Use a secure production secret store
- Enforce HTTPS
- Apply least privilege to users, services, and data access
## Authentication And Authorization
Authentication answers who the user or caller is. Authorization answers what they can do.
Default pipeline order:
1. `UseAuthentication()`
2. `UseAuthorization()`
Apply authorization at boundaries:
- `[Authorize]` on controllers, actions, page models, or hubs
- `RequireAuthorization()` on endpoints and route groups
- policies for reusable rules
- roles only when role-based checks are actually the right abstraction
Use `AllowAnonymous` sparingly and intentionally.
## Identity
Use ASP.NET Core Identity when the app needs first-party user accounts, login flows, password management, email confirmation, MFA, or related account management.
Useful starting points:
- `dotnet new webapp -au Individual`
- `dotnet new mvc -au Individual`
Identity guidance:
- scaffold only the pages you truly need to customize
- keep Identity UI updates maintainable; full scaffolding increases merge and upgrade cost
- use policies and claims for authorization rather than encoding all decisions in page logic
- persist data-protection keys appropriately in multi-instance deployments
On ASP.NET Core 10, Identity metrics are available for observing auth-related behavior. Use them when the app has meaningful authentication traffic or security monitoring requirements.
## CSRF, CORS, And Browser Security
- Use antiforgery protection for cookie-based interactive apps and form posts
- Do not confuse CORS with authentication or authorization
- Avoid permissive `AllowAnyOrigin` plus credentials combinations
- Treat browser-side state as untrusted
## HTTPS, HSTS, And Forwarded Headers
- redirect HTTP to HTTPS
- enable HSTS outside development when appropriate
- configure forwarded headers correctly when behind proxies or load balancers
- do not generate links or evaluate scheme-sensitive behavior before proxy headers are processed
## Data Protection And Secrets
- persist data-protection keys outside ephemeral local storage when the app runs on multiple instances
- do not use environment variables as the preferred long-term home for production secrets when a stronger secret store is available
- never check production credentials into source control
## Blazor Note
For Blazor apps, read the general ASP.NET Core security guidance first and then the Blazor-specific security docs. Some Blazor security guidance adds to or supersedes the general guidance.
@@ -0,0 +1,43 @@
# ASP.NET Core Source Map
This skill is synthesized from the official ASP.NET Core documentation tree and overview pages. Use this file to map a task to the corresponding Microsoft Learn area before opening deeper docs.
Core sources:
- https://learn.microsoft.com/aspnet/core/
- https://raw.githubusercontent.com/dotnet/AspNetCore.Docs/main/aspnetcore/toc.yml
- https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore
## Documentation Tree Mapping
| ASP.NET Core docs area | Use this skill reference first |
| --- | --- |
| Overview, Get started, What's new | `stack-selection.md`, `versioning-and-upgrades.md` |
| Fundamentals | `program-and-pipeline.md` |
| Web apps | `ui-blazor.md`, `ui-razor-pages.md`, `ui-mvc.md` |
| APIs | `apis-minimal-and-controllers.md` |
| Real-time apps | `realtime-grpc-and-background-work.md` |
| Remote Procedure Call apps | `realtime-grpc-and-background-work.md` |
| Servers, Host and deploy | `testing-performance-and-operations.md` |
| Test, Debug, Troubleshoot | `testing-performance-and-operations.md` |
| Data access | `data-state-and-services.md` |
| Security and Identity | `security-and-identity.md` |
| Performance | `testing-performance-and-operations.md` |
| Migration and updates | `versioning-and-upgrades.md` |
## Areas To Consult Directly On Microsoft Learn
The following topics are part of the ASP.NET Core documentation tree but are not expanded into their own dedicated reference file here:
- globalization and localization
- advanced hosting and YARP details
- debugger and diagnostics tooling specifics
- narrow API-reference pages for individual types
When a task is dominated by one of those areas, go straight to the matching Microsoft Learn section after checking the reference files in this skill.
## Practical Deep-Dive Rule
- Start with the focused reference in this skill
- If the task depends on a narrow platform detail, open the matching Learn article
- If the task depends on version-specific behavior, confirm the correct moniker or breaking-changes page
@@ -0,0 +1,63 @@
# Stack Selection
Primary docs:
- https://learn.microsoft.com/aspnet/core/
- https://learn.microsoft.com/aspnet/core/blazor/
- https://learn.microsoft.com/aspnet/core/razor-pages/
- https://learn.microsoft.com/aspnet/core/mvc/overview
- https://learn.microsoft.com/aspnet/core/web-api/
- https://learn.microsoft.com/aspnet/core/fundamentals/minimal-apis
## Default Version Choice
- Prefer the latest stable .NET and ASP.NET Core for new production work.
- As of March 2026, that means `net10.0` unless the repository or user request says otherwise.
- Treat ASP.NET Core 11 as preview. Do not adopt preview APIs by default.
- If the repository already targets `net8.0`, `net9.0`, or another framework, stay within that target unless the task is explicitly an upgrade.
## Template Short Names
The current .NET 10 SDK templates include:
- `dotnet new blazor`
- `dotnet new webapp`
- `dotnet new mvc`
- `dotnet new webapi`
- `dotnet new webapiaot`
- `dotnet new grpc`
- `dotnet new web`
- `dotnet new razorclasslib`
Verify template names with `dotnet new list` if the environment differs.
## Application Model Matrix
| Model | Prefer when | Watch out for | Typical starting point |
| --- | --- | --- | --- |
| Blazor Web App | Build full-stack .NET UI with SSR plus optional interactivity | Interactive server needs a live connection; WebAssembly increases payload size | `dotnet new blazor` |
| Razor Pages | Build page-focused CRUD, forms, dashboards, and line-of-business apps | Authorization cannot be applied per page handler; use MVC if handler-level control matters | `dotnet new webapp` |
| MVC | Build large server-rendered apps with clear controller/view separation, filters, and action-based patterns | More ceremony than Razor Pages for simple page flows | `dotnet new mvc` |
| Minimal APIs | Build focused HTTP APIs, internal services, lightweight backends, and small surface areas | Route handlers can become hard to manage if business logic or metadata grows without structure | `dotnet new webapi` or `dotnet new web` |
| Controller-based Web API | Build APIs that benefit from `[ApiController]`, content negotiation, filters, formatters, and mature controller conventions | More ceremony than Minimal APIs for small endpoints | `dotnet new webapi` |
| SignalR | Add server push, live updates, chat, collaborative UI, or notifications | Requires connection lifecycle management and scale-out planning | Add to an existing ASP.NET Core app |
| gRPC | Build service-to-service or streaming RPC over HTTP/2 | Browser support is different from ordinary JSON APIs; use gRPC-Web only when needed | `dotnet new grpc` |
## Fast Heuristics
- Choose Blazor Web App when the UI itself should be a .NET component model.
- Choose Razor Pages when the app is mostly page and form oriented.
- Choose MVC when actions, views, filters, and controller conventions are the center of the design.
- Choose Minimal APIs first for small to medium HTTP services.
- Switch to controllers when the API needs richer attribute-driven behavior, custom formatters, or strong alignment with existing MVC/Web API conventions.
- Keep the current app model in an existing codebase unless the mismatch is causing real complexity.
## Mixed-Model Guidance
ASP.NET Core can mix models in one host. Common combinations:
- Razor Pages or MVC for server-rendered UI plus Minimal APIs for AJAX or mobile endpoints
- Blazor Web App plus Minimal APIs for external integration endpoints
- MVC or Razor Pages plus SignalR for live updates
- Web API plus gRPC for internal service-to-service calls
Mix models only when it simplifies the public surface. Do not add a second app model just because ASP.NET Core allows it.
@@ -0,0 +1,92 @@
# Testing, Performance, And Operations
Primary docs:
- https://learn.microsoft.com/aspnet/core/test/integration-tests
- https://learn.microsoft.com/aspnet/core/host-and-deploy/
- https://learn.microsoft.com/aspnet/core/host-and-deploy/health-checks
- https://learn.microsoft.com/aspnet/core/performance/
## Testing Strategy
Use layered testing instead of relying on one style:
- unit tests for pure services and business logic
- integration tests for request pipeline, DI, database, auth, and framework wiring
- browser tests for end-to-end user flows
## Integration Tests
Use `Microsoft.AspNetCore.Mvc.Testing` and `WebApplicationFactory<Program>` for integration tests.
Guidance from the official docs:
- use a test host and `HttpClient`
- replace services with test doubles when needed
- control redirects when asserting auth behavior
- handle antiforgery correctly for form posts
- prefer SQLite in-memory over the EF Core in-memory provider for more realistic database tests
For SPA or browser-driven scenarios, Microsoft recommends browser automation such as Playwright for .NET.
## Performance Defaults
Reach for built-in features before custom optimization layers:
- output caching
- response caching where appropriate
- response compression
- HTTP request timeouts
- rate limiting
- static file handling
General performance guidance:
- measure first
- keep database and network round trips visible
- reduce payload size
- use streaming or pagination when data is large
- keep synchronous blocking out of hot paths
## Health Checks And Observability
Add health checks for dependencies that matter operationally.
Use separate checks or tags when you need:
- liveness
- readiness
- dependency-specific health surfaces
Also ensure:
- structured logs
- request tracing where applicable
- metrics for critical paths such as auth, API latency, and background work
## Hosting And Deployment
Typical deployment flow:
1. `dotnet publish`
2. deploy the publish output
3. run behind a process manager
4. place a reverse proxy in front when the environment requires it
Know the deployment environment:
- IIS or Windows Service on Windows
- Kestrel plus Nginx or another reverse proxy on Linux
- container hosting when the platform expects it
Behind proxies or load balancers:
- configure forwarded headers
- validate scheme, host, and remote IP behavior
- test auth redirects and callback URLs in the deployed topology
## Operational Safeguards
- add health checks for databases and critical external services
- fail fast on invalid configuration where possible
- keep secrets out of publish artifacts
- verify data-protection key persistence in multi-instance deployments
@@ -0,0 +1,53 @@
# Blazor
Primary docs:
- https://learn.microsoft.com/aspnet/core/blazor/
- https://learn.microsoft.com/aspnet/core/blazor/fundamentals/
- https://learn.microsoft.com/aspnet/core/blazor/security/
## Choose Blazor Deliberately
Prefer Blazor when the UI itself should be built as reusable .NET components and the team wants a full-stack .NET model.
Current guidance centers on the Blazor Web App model, which can combine:
- static SSR for fast first render
- interactive server rendering
- interactive WebAssembly rendering
- per-component render mode choices
Use standalone Blazor WebAssembly only when the app is intentionally client-heavy or must run as static files without a server-rendered host.
## Render Mode Heuristics
- Start with static SSR when the page is mostly read-only and fast first paint matters
- Use interactive server rendering when you want rich interactivity without shipping the full .NET runtime to the browser
- Use interactive WebAssembly when offline capability, client-side execution, or browser-local compute is the point
- Mix render modes only when the split is clear and justified
## Component Patterns
- Keep components focused and composable
- Move data access and business rules into injected services
- Pass data through parameters, not hidden global state
- Use forms and validation with Blazor's built-in editing and validation components
- Prefer shared Razor Class Libraries for reusable component sets
## Data And Interactivity
- Use DI in components with restraint; avoid turning components into service locators
- Treat JS interop as an edge mechanism for browser APIs or third-party libraries, not the primary application model
- Keep long-running work off the UI event path
- Be deliberate about prerendering, streaming rendering, and enhanced navigation when they improve perceived performance
## Security Notes
- Follow the general ASP.NET Core security guidance first, then load the Blazor-specific docs for details that supersede it
- Remember that client-side code and browser state are not trusted
- Keep secrets and privileged operations on the server
- Use authorization-aware UI only as a convenience layer; enforce rules on the server as well
## When Not To Use Blazor
- Do not force Blazor onto a mostly conventional server-rendered app that already fits Razor Pages or MVC well
- Do not choose WebAssembly by default for small interaction needs that SSR or interactive server rendering handles more simply
@@ -0,0 +1,56 @@
# MVC
Primary docs:
- https://learn.microsoft.com/aspnet/core/mvc/overview
- https://learn.microsoft.com/aspnet/core/mvc/controllers/
- https://learn.microsoft.com/aspnet/core/mvc/views/
## Choose MVC When Actions And Views Matter
Prefer MVC when the application benefits from explicit controllers, action-based routing, filters, view models, and a strong separation between orchestration and presentation.
This is often the right fit for:
- large server-rendered sites
- applications with many cross-cutting filters or action conventions
- applications that mix views and APIs in the same controller layer
- teams already organized around controllers and views
## Core Shape
Enable MVC with views using:
- `builder.Services.AddControllersWithViews();`
- `app.MapControllerRoute(...)`
Keep views focused on presentation. Keep controllers focused on HTTP orchestration. Put business rules in services.
## Controller Guidance
- Derive from `Controller` when the controller returns views
- Keep actions small and explicit
- Use model binding and validation instead of manual request parsing
- Return view models, not EF entities, to views
- Use POST-Redirect-GET for form submissions
## View Guidance
- Use layouts, partial views, and Tag Helpers to keep markup consistent
- Keep complex display logic out of Razor markup when it becomes hard to follow
- Use strongly typed view models
- Avoid coupling views directly to persistence models
## Structure And Scale
- Use areas for large bounded sections such as Admin or BackOffice
- Keep route conventions explicit
- Apply filters when behavior truly belongs at the MVC layer
- Avoid giant god controllers; split by cohesive feature or resource
## Choosing MVC Over Razor Pages
Prefer MVC over Razor Pages when:
- multiple related actions share controller-level behavior
- handler-level authorization or action filters matter
- URL and action design are more natural than page-file routing
@@ -0,0 +1,55 @@
# Razor Pages
Primary docs:
- https://learn.microsoft.com/aspnet/core/razor-pages/
- https://learn.microsoft.com/aspnet/core/tutorials/razor-pages/
## Choose Razor Pages For Page-Centered Apps
Prefer Razor Pages when requests naturally map to pages, forms, and page-level handlers. This is a strong default for internal tools, CRUD apps, account flows, and admin surfaces.
## Core Shape
Enable Razor Pages with:
- `builder.Services.AddRazorPages();`
- `app.MapRazorPages();`
Use the `@page` directive to turn a `.cshtml` file into an endpoint. Keep request logic in the paired `PageModel` class when the page is more than trivial.
## Routing Model
- File system location defines the route by default
- `Pages/Index.cshtml` maps to `/`
- `Pages/Store/Index.cshtml` maps to `/Store`
- Keep folder structure meaningful because it becomes the URL structure
## PageModel Guidance
- Use `OnGet`, `OnPost`, and named handlers for request processing
- Use bindable properties and model validation for forms
- Keep page models thin; move business logic into injected services
- Use Tag Helpers and model binding instead of manual request parsing
## Good Fits
- form-heavy workflows
- dashboards and back-office applications
- simple content with server-side validation
- applications where a page is the primary navigation unit
## Key Limitation
Do not rely on per-handler authorization with Razor Pages. Microsoft explicitly recommends using MVC controllers when different handlers on the same logical surface need different authorization behavior.
Preferred responses to that limitation:
- split the handlers into separate pages
- move the surface to MVC if action-level authorization is a better fit
## Organizational Guidance
- Group related pages into folders
- Use partial views for repeated fragments
- Use areas only when the application has clear bounded sections
- Keep shared layout and page conventions centralized
@@ -0,0 +1,51 @@
# Versioning And Upgrades
Primary docs:
- https://learn.microsoft.com/aspnet/core/release-notes/
- https://learn.microsoft.com/aspnet/core/release-notes/aspnetcore-10.0
- https://learn.microsoft.com/aspnet/core/release-notes/aspnetcore-9.0
- https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/breaking-changes
## Versioning Default
- For new production apps in March 2026, prefer `net10.0`
- For existing apps, match the repository's target framework unless the task is explicitly an upgrade
- Before using a new API, confirm it exists in the target framework
## Upgrade Workflow
1. Identify the current target framework and SDK
2. Read the "What's new" and breaking-changes pages for each version hop
3. Compile and resolve obsoletions intentionally
4. Re-run integration tests and auth flows
5. Re-test deployment-specific behavior such as proxies, cookies, and static assets
## High-Value Breaking-Change Checks
When moving to ASP.NET Core 10, watch for:
- cookie login redirects disabled for known API endpoints
- `WithOpenApi` deprecation
- `WebHostBuilder`, `IWebHost`, and `WebHost` obsolescence
- Razor runtime compilation obsolescence
When moving to ASP.NET Core 9, watch for:
- `ValidateOnBuild` and `ValidateScopes` enabled in development when using `HostBuilder`
- middleware constructor expectations and DI validation changes
When moving to ASP.NET Core 8, watch for:
- Minimal API `IFormFile` antiforgery requirements
- `AddRateLimiter()` and `AddHttpLogging()` requirements when corresponding middleware is used
## Migration Principles
- Prefer migration to the modern hosting model when touching startup extensively
- Remove compatibility shims only after tests confirm behavior
- Avoid mixing new framework idioms with old startup architecture in a half-migrated state
- Keep one authoritative target framework in project files unless multi-targeting is deliberate
## Preview Feature Rule
Do not introduce preview-only APIs or docs guidance unless the user explicitly asks for preview adoption or the repository is already on preview SDKs.
@@ -0,0 +1,41 @@
---
name: aspnet-minimal-api-openapi
description: 'Create ASP.NET Minimal API endpoints with proper OpenAPI documentation'
---
# ASP.NET Minimal API with OpenAPI
Your goal is to help me create well-structured ASP.NET Minimal API endpoints with correct types and comprehensive OpenAPI/Swagger documentation.
## API Organization
- Group related endpoints using `MapGroup()` extension
- Use endpoint filters for cross-cutting concerns
- Structure larger APIs with separate endpoint classes
- Consider using a feature-based folder structure for complex APIs
## Request and Response Types
- Define explicit request and response DTOs/models
- Create clear model classes with proper validation attributes
- Use record types for immutable request/response objects
- Use meaningful property names that align with API design standards
- Apply `[Required]` and other validation attributes to enforce constraints
- Use the ProblemDetailsService and StatusCodePages to get standard error responses
## Type Handling
- Use strongly-typed route parameters with explicit type binding
- Use `Results<T1, T2>` to represent multiple response types
- Return `TypedResults` instead of `Results` for strongly-typed responses
- Leverage C# 10+ features like nullable annotations and init-only properties
## OpenAPI Documentation
- Use the built-in OpenAPI document support added in .NET 9
- Define operation summary and description
- Add operationIds using the `WithName` extension method
- Add descriptions to properties and parameters with `[Description()]`
- Set proper content types for requests and responses
- Use document transformers to add elements like servers, tags, and security schemes
- Use schema transformers to apply customizations to OpenAPI schemas
@@ -0,0 +1,392 @@
---
name: containerize-aspnetcore
description: 'Containerize an ASP.NET Core project by creating Dockerfile and .dockerfile files customized for the project.'
---
# ASP.NET Core Docker Containerization Prompt
## Containerization Request
Containerize the ASP.NET Core (.NET) project specified in the settings below, focusing **exclusively** on changes required for the application to run in a Linux Docker container. Containerization should consider all settings specified here.
Abide by best practices for containerizing .NET Core applications, ensuring that the container is optimized for performance, security, and maintainability.
## Containerization Settings
This section of the prompt contains the specific settings and configurations required for containerizing the ASP.NET Core application. Prior to running this prompt, ensure that the settings are filled out with the necessary information. Note that in many cases, only the first few settings are required. Later settings can be left as defaults if they do not apply to the project being containerized.
Any settings that are not specified will be set to default values. The default values are provided in `[square brackets]`.
### Basic Project Information
1. Project to containerize:
- `[ProjectName (provide path to .csproj file)]`
2. .NET version to use:
- `[8.0 or 9.0 (Default 8.0)]`
3. Linux distribution to use:
- `[debian, alpine, ubuntu, chiseled, or Azure Linux (mariner) (Default debian)]`
4. Custom base image for the build stage of the Docker image ("None" to use standard Microsoft base image):
- `[Specify base image to use for build stage (Default None)]`
5. Custom base image for the run stage of the Docker image ("None" to use standard Microsoft base image):
- `[Specify base image to use for run stage (Default None)]`
### Container Configuration
1. Ports that must be exposed in the container image:
- Primary HTTP port: `[e.g., 8080]`
- Additional ports: `[List any additional ports, or "None"]`
2. User account the container should run as:
- `[User account, or default to "$APP_UID"]`
3. Application URL configuration:
- `[Specify ASPNETCORE_URLS, or default to "http://+:8080"]`
### Build configuration
1. Custom build steps that must be performed before building the container image:
- `[List any specific build steps, or "None"]`
2. Custom build steps that must be performed after building the container image:
- `[List any specific build steps, or "None"]`
3. NuGet package sources that must be configured:
- `[List any private NuGet feeds with authentication details, or "None"]`
### Dependencies
1. System packages that must be installed in the container image:
- `[Package names for the chosen Linux distribution, or "None"]`
2. Native libraries that must be copied to the container image:
- `[Library names and paths, or "None"]`
3. Additional .NET tools that must be installed:
- `[Tool names and versions, or "None"]`
### System Configuration
1. Environment variables that must be set in the container image:
- `[Variable names and values, or "Use defaults"]`
### File System
1. Files/directories that need to be copied to the container image:
- `[Paths relative to project root, or "None"]`
- Target location in container: `[Container paths, or "Not applicable"]`
2. Files/directories to exclude from containerization:
- `[Paths to exclude, or "None"]`
3. Volume mount points that should be configured:
- `[Volume paths for persistent data, or "None"]`
### .dockerignore Configuration
1. Patterns to include in the `.dockerignore` file (.dockerignore will already have common defaults; these are additional patterns):
- Additional patterns: `[List any additional patterns, or "None"]`
### Health Check Configuration
1. Health check endpoint:
- `[Health check URL path, or "None"]`
2. Health check interval and timeout:
- `[Interval and timeout values, or "Use defaults"]`
### Additional Instructions
1. Other instructions that must be followed to containerize the project:
- `[Specific requirements, or "None"]`
2. Known issues to address:
- `[Describe any known issues, or "None"]`
## Scope
- ✅ App configuration modification to ensure application settings and connection strings can be read from environment variables
- ✅ Dockerfile creation and configuration for an ASP.NET Core application
- ✅ Specifying multiple stages in the Dockerfile to build/publish the application and copy the output to the final image
- ✅ Configuration of Linux container platform compatibility (Alpine, Ubuntu, Chiseled, or Azure Linux (Mariner))
- ✅ Proper handling of dependencies (system packages, native libraries, additional tools)
- ❌ No infrastructure setup (assumed to be handled separately)
- ❌ No code changes beyond those required for containerization
## Execution Process
1. Review the containerization settings above to understand the containerization requirements
2. Create a `progress.md` file to track changes with check marks
3. Determine the .NET version from the project's .csproj file by checking the `TargetFramework` element
4. Select the appropriate Linux container image based on:
- The .NET version detected from the project
- The Linux distribution specified in containerization settings (Alpine, Ubuntu, Chiseled, or Azure Linux (Mariner))
- If the user does not request specific base images in the containerization settings, then the base images MUST be valid mcr.microsoft.com/dotnet images with a tag as shown in the example Dockerfile, below, or in documentation
- Official Microsoft .NET images for build and runtime stages:
- SDK image tags (for build stage): https://github.com/dotnet/dotnet-docker/blob/main/README.sdk.md
- ASP.NET Core runtime image tags: https://github.com/dotnet/dotnet-docker/blob/main/README.aspnet.md
- .NET runtime image tags: https://github.com/dotnet/dotnet-docker/blob/main/README.runtime.md
5. Create a Dockerfile in the root of the project directory to containerize the application
- The Dockerfile should use multiple stages:
- Build stage: Use a .NET SDK image to build the application
- Copy csproj file(s) first
- Copy NuGet.config if one exists and configure any private feeds
- Restore NuGet packages
- Then, copy the rest of the source code and build and publish the application to /app/publish
- Final stage: Use the selected .NET runtime image to run the application
- Set the working directory to /app
- Set the user as directed (by default, to a non-root user (e.g., `$APP_UID`))
- Unless directed otherwise in containerization settings, a new user does *not* need to be created. Use the `$APP_UID` variable to specify the user account.
- Copy the published output from the build stage to the final image
- Be sure to consider all requirements in the containerization settings:
- .NET version and Linux distribution
- Exposed ports
- User account for container
- ASPNETCORE_URLS configuration
- System package installation
- Native library dependencies
- Additional .NET tools
- Environment variables
- File/directory copying
- Volume mount points
- Health check configuration
6. Create a `.dockerignore` file in the root of the project directory to exclude unnecessary files from the Docker image. The `.dockerignore` file **MUST** include at least the following elements as well as additional patterns as specified in the containerization settings:
- bin/
- obj/
- .dockerignore
- Dockerfile
- .git/
- .github/
- .vs/
- .vscode/
- **/node_modules/
- *.user
- *.suo
- **/.DS_Store
- **/Thumbs.db
- Any additional patterns specified in the containerization settings
7. Configure health checks if specified in the containerization settings:
- Add HEALTHCHECK instruction to Dockerfile if health check endpoint is provided
- Use curl or wget to check the health endpoint
8. Mark tasks as completed: [ ] → [✓]
9. Continue until all tasks are complete and Docker build succeeds
## Build and Runtime Verification
Confirm that Docker build succeeds once the Dockerfile is completed. Use the following command to build the Docker image:
```bash
docker build -t aspnetcore-app:latest .
```
If the build fails, review the error messages and make necessary adjustments to the Dockerfile or project configuration. Report success/failure.
## Progress Tracking
Maintain a `progress.md` file with the following structure:
```markdown
# Containerization Progress
## Environment Detection
- [ ] .NET version detection (version: ___)
- [ ] Linux distribution selection (distribution: ___)
## Configuration Changes
- [ ] Application configuration verification for environment variable support
- [ ] NuGet package source configuration (if applicable)
## Containerization
- [ ] Dockerfile creation
- [ ] .dockerignore file creation
- [ ] Build stage created with SDK image
- [ ] csproj file(s) copied for package restore
- [ ] NuGet.config copied if applicable
- [ ] Runtime stage created with runtime image
- [ ] Non-root user configuration
- [ ] Dependency handling (system packages, native libraries, tools, etc.)
- [ ] Health check configuration (if applicable)
- [ ] Special requirements implementation
## Verification
- [ ] Review containerization settings and make sure that all requirements are met
- [ ] Docker build success
```
Do not pause for confirmation between steps. Continue methodically until the application has been containerized and Docker build succeeds.
**YOU ARE NOT DONE UNTIL ALL CHECKBOXES ARE MARKED!** This includes building the Docker image successfully and addressing any issues that arise during the build process.
## Example Dockerfile
An example Dockerfile for an ASP.NET Core (.NET) application using a Linux base image.
```dockerfile
# ============================================================
# Stage 1: Build and publish the application
# ============================================================
# Base Image - Select the appropriate .NET SDK version and Linux distribution
# Possible tags include:
# - 8.0-bookworm-slim (Debian 12)
# - 8.0-noble (Ubuntu 24.04)
# - 8.0-alpine (Alpine Linux)
# - 9.0-bookworm-slim (Debian 12)
# - 9.0-noble (Ubuntu 24.04)
# - 9.0-alpine (Alpine Linux)
# Uses the .NET SDK image for building the application
FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
# Copy project files first for better caching
COPY ["YourProject/YourProject.csproj", "YourProject/"]
COPY ["YourOtherProject/YourOtherProject.csproj", "YourOtherProject/"]
# Copy NuGet configuration if it exists
COPY ["NuGet.config", "."]
# Restore NuGet packages
RUN dotnet restore "YourProject/YourProject.csproj"
# Copy source code
COPY . .
# Perform custom pre-build steps here, if needed
# RUN echo "Running pre-build steps..."
# Build and publish the application
WORKDIR "/src/YourProject"
RUN dotnet build "YourProject.csproj" -c $BUILD_CONFIGURATION -o /app/build
# Publish the application
RUN dotnet publish "YourProject.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
# Perform custom post-build steps here, if needed
# RUN echo "Running post-build steps..."
# ============================================================
# Stage 2: Final runtime image
# ============================================================
# Base Image - Select the appropriate .NET runtime version and Linux distribution
# Possible tags include:
# - 8.0-bookworm-slim (Debian 12)
# - 8.0-noble (Ubuntu 24.04)
# - 8.0-alpine (Alpine Linux)
# - 8.0-noble-chiseled (Ubuntu 24.04 Chiseled)
# - 8.0-azurelinux3.0 (Azure Linux)
# - 9.0-bookworm-slim (Debian 12)
# - 9.0-noble (Ubuntu 24.04)
# - 9.0-alpine (Alpine Linux)
# - 9.0-noble-chiseled (Ubuntu 24.04 Chiseled)
# - 9.0-azurelinux3.0 (Azure Linux)
# Uses the .NET runtime image for running the application
FROM mcr.microsoft.com/dotnet/aspnet:8.0-bookworm-slim AS final
# Install system packages if needed (uncomment and modify as needed)
# RUN apt-get update && apt-get install -y \
# curl \
# wget \
# ca-certificates \
# libgdiplus \
# && rm -rf /var/lib/apt/lists/*
# Install additional .NET tools if needed (uncomment and modify as needed)
# RUN dotnet tool install --global dotnet-ef --version 8.0.0
# ENV PATH="$PATH:/root/.dotnet/tools"
WORKDIR /app
# Copy published application from build stage
COPY --from=build /app/publish .
# Copy additional files if needed (uncomment and modify as needed)
# COPY ./config/appsettings.Production.json .
# COPY ./certificates/ ./certificates/
# Set environment variables
ENV ASPNETCORE_ENVIRONMENT=Production
ENV ASPNETCORE_URLS=http://+:8080
# Add custom environment variables if needed (uncomment and modify as needed)
# ENV CONNECTIONSTRINGS__DEFAULTCONNECTION="your-connection-string"
# ENV FEATURE_FLAG_ENABLED=true
# Configure SSL/TLS certificates if needed (uncomment and modify as needed)
# ENV ASPNETCORE_Kestrel__Certificates__Default__Path=/app/certificates/app.pfx
# ENV ASPNETCORE_Kestrel__Certificates__Default__Password=your_password
# Expose the port the application listens on
EXPOSE 8080
# EXPOSE 8081 # Uncomment if using HTTPS
# Install curl for health checks if not already present
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
# Configure health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
# Create volumes for persistent data if needed (uncomment and modify as needed)
# VOLUME ["/app/data", "/app/logs"]
# Switch to non-root user for security
USER $APP_UID
# Set the entry point for the application
ENTRYPOINT ["dotnet", "YourProject.dll"]
```
## Adapting this Example
**Note:** Customize this template based on the specific requirements in containerization settings.
When adapting this example Dockerfile:
1. Replace `YourProject.csproj`, `YourProject.dll`, etc. with your actual project names
2. Adjust the .NET version and Linux distribution as needed
3. Modify the dependency installation steps based on your requirements and remove any unnecessary ones
4. Configure environment variables specific to your application
5. Add or remove stages as needed for your specific workflow
6. Update the health check endpoint to match your application's health check route
## Linux Distribution Variations
### Alpine Linux
For smaller image sizes, you can use Alpine Linux:
```dockerfile
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
# ... build steps ...
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS final
# Install packages using apk
RUN apk update && apk add --no-cache curl ca-certificates
```
### Ubuntu Chiseled
For minimal attack surface, consider using chiseled images:
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:8.0-jammy-chiseled AS final
# Note: Chiseled images have minimal packages, so you may need to use a different base for additional dependencies
```
### Azure Linux (Mariner)
For Azure-optimized containers:
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:8.0-azurelinux3.0 AS final
# Install packages using tdnf
RUN tdnf update -y && tdnf install -y curl ca-certificates && tdnf clean all
```
## Notes on Stage Naming
- The `AS stage-name` syntax gives each stage a name
- Use `--from=stage-name` to copy files from a previous stage
- You can have multiple intermediate stages that aren't used in the final image
- The `final` stage is the one that becomes the final container image
## Security Best Practices
- Always run as a non-root user in production
- Use specific image tags instead of `latest`
- Minimize the number of installed packages
- Keep base images updated
- Use multi-stage builds to exclude build dependencies from the final image
@@ -0,0 +1,49 @@
---
name: csharp-async
description: 'Get best practices for C# async programming'
---
# C# Async Programming Best Practices
Your goal is to help me follow best practices for asynchronous programming in C#.
## Naming Conventions
- Use the 'Async' suffix for all async methods
- Match method names with their synchronous counterparts when applicable (e.g., `GetDataAsync()` for `GetData()`)
## Return Types
- Return `Task<T>` when the method returns a value
- Return `Task` when the method doesn't return a value
- Consider `ValueTask<T>` for high-performance scenarios to reduce allocations
- Avoid returning `void` for async methods except for event handlers
## Exception Handling
- Use try/catch blocks around await expressions
- Avoid swallowing exceptions in async methods
- Use `ConfigureAwait(false)` when appropriate to prevent deadlocks in library code
- Propagate exceptions with `Task.FromException()` instead of throwing in async Task returning methods
## Performance
- Use `Task.WhenAll()` for parallel execution of multiple tasks
- Use `Task.WhenAny()` for implementing timeouts or taking the first completed task
- Avoid unnecessary async/await when simply passing through task results
- Consider cancellation tokens for long-running operations
## Common Pitfalls
- Never use `.Wait()`, `.Result`, or `.GetAwaiter().GetResult()` in async code
- Avoid mixing blocking and async code
- Don't create async void methods (except for event handlers)
- Always await Task-returning methods
## Implementation Patterns
- Implement the async command pattern for long-running operations
- Use async streams (IAsyncEnumerable<T>) for processing sequences asynchronously
- Consider the task-based asynchronous pattern (TAP) for public APIs
When reviewing my C# code, identify these issues and suggest improvements that follow these best practices.
@@ -0,0 +1,62 @@
---
name: csharp-docs
description: 'Ensure that C# types are documented with XML comments and follow best practices for documentation.'
---
# C# Documentation Best Practices
- Public members should be documented with XML comments.
- It is encouraged to document internal members as well, especially if they are complex or not self-explanatory.
## Guidance for all APIs
- Use `<summary>` to provide a brief, one sentence, description of what the type or member does. Start the summary with a present-tense, third-person verb.
- Use `<remarks>` for additional information, which can include implementation details, usage notes, or any other relevant context.
- Use `<see langword>` for language-specific keywords like `null`, `true`, `false`, `int`, `bool`, etc.
- Use `<c>` for inline code snippets.
- Use `<example>` for usage examples on how to use the member.
- Use `<code>` for code blocks. `<code>` tags should be placed within an `<example>` tag. Add the language of the code example using the `language` attribute, for example, `<code language="csharp">`.
- Use `<see cref>` to reference other types or members inline (in a sentence).
- Use `<seealso>` for standalone (not in a sentence) references to other types or members in the "See also" section of the online docs.
- Use `<inheritdoc/>` to inherit documentation from base classes or interfaces.
- Unless there is major behavior change, in which case you should document the differences.
## Methods
- Use `<param>` to describe method parameters.
- The description should be a noun phrase that doesn't specify the data type.
- Begin with an introductory article.
- If the parameter is a flag enum, start the description with "A bitwise combination of the enumeration values that specifies...".
- If the parameter is a non-flag enum, start the description with "One of the enumeration values that specifies...".
- If the parameter is a Boolean, the wording should be of the form "`<see langword="true" />` to ...; otherwise, `<see langword="false" />`.".
- If the parameter is an "out" parameter, the wording should be of the form "When this method returns, contains .... This parameter is treated as uninitialized.".
- Use `<paramref>` to reference parameter names in documentation.
- Use `<typeparam>` to describe type parameters in generic types or methods.
- Use `<typeparamref>` to reference type parameters in documentation.
- Use `<returns>` to describe what the method returns.
- The description should be a noun phrase that doesn't specify the data type.
- Begin with an introductory article.
- If the return type is Boolean, the wording should be of the form "`<see langword="true" />` if ...; otherwise, `<see langword="false" />`.".
## Constructors
- The summary wording should be "Initializes a new instance of the <Class> class [or struct].".
## Properties
- The `<summary>` should start with:
- "Gets or sets..." for a read-write property.
- "Gets..." for a read-only property.
- "Gets [or sets] a value that indicates whether..." for properties that return a Boolean value.
- Use `<value>` to describe the value of the property.
- The description should be a noun phrase that doesn't specify the data type.
- If the property has a default value, add it in a separate sentence, for example, "The default is `<see langword="false" />`".
- If the value type is Boolean, the wording should be of the form "`<see langword="true" />` if ...; otherwise, `<see langword="false" />`. The default is ...".
## Exceptions
- Use `<exception cref>` to document exceptions thrown by constructors, properties, indexers, methods, operators, and events.
- Document all exceptions thrown directly by the member.
- For exceptions thrown by nested members, document only the exceptions users are most likely to encounter.
- The description of the exception describes the condition under which it's thrown.
- Omit "Thrown if ..." or "If ..." at the beginning of the sentence. Just state the condition directly, for example "An error occurred when accessing a Message Queuing API."
@@ -0,0 +1,478 @@
---
name: csharp-mstest
description: 'Get best practices for MSTest 3.x/4.x unit testing, including modern assertion APIs and data-driven tests'
---
# MSTest Best Practices (MSTest 3.x/4.x)
Your goal is to help me write effective unit tests with modern MSTest, using current APIs and best practices.
## Project Setup
- Use a separate test project with naming convention `[ProjectName].Tests`
- Reference MSTest 3.x+ NuGet packages (includes analyzers)
- Consider using MSTest.Sdk for simplified project setup
- Run tests with `dotnet test`
## Test Class Structure
- Use `[TestClass]` attribute for test classes
- **Seal test classes by default** for performance and design clarity
- Use `[TestMethod]` for test methods (prefer over `[DataTestMethod]`)
- Follow Arrange-Act-Assert (AAA) pattern
- Name tests using pattern `MethodName_Scenario_ExpectedBehavior`
```csharp
[TestClass]
public sealed class CalculatorTests
{
[TestMethod]
public void Add_TwoPositiveNumbers_ReturnsSum()
{
// Arrange
var calculator = new Calculator();
// Act
var result = calculator.Add(2, 3);
// Assert
Assert.AreEqual(5, result);
}
}
```
## Test Lifecycle
- **Prefer constructors over `[TestInitialize]`** - enables `readonly` fields and follows standard C# patterns
- Use `[TestCleanup]` for cleanup that must run even if test fails
- Combine constructor with async `[TestInitialize]` when async setup is needed
```csharp
[TestClass]
public sealed class ServiceTests
{
private readonly MyService _service; // readonly enabled by constructor
public ServiceTests()
{
_service = new MyService();
}
[TestInitialize]
public async Task InitAsync()
{
// Use for async initialization only
await _service.WarmupAsync();
}
[TestCleanup]
public void Cleanup() => _service.Reset();
}
```
### Execution Order
1. **Assembly Initialization** - `[AssemblyInitialize]` (once per test assembly)
2. **Class Initialization** - `[ClassInitialize]` (once per test class)
3. **Test Initialization** (for every test method):
1. Constructor
2. Set `TestContext` property
3. `[TestInitialize]`
4. **Test Execution** - test method runs
5. **Test Cleanup** (for every test method):
1. `[TestCleanup]`
2. `DisposeAsync` (if implemented)
3. `Dispose` (if implemented)
6. **Class Cleanup** - `[ClassCleanup]` (once per test class)
7. **Assembly Cleanup** - `[AssemblyCleanup]` (once per test assembly)
## Modern Assertion APIs
MSTest provides three assertion classes: `Assert`, `StringAssert`, and `CollectionAssert`.
### Assert Class - Core Assertions
```csharp
// Equality
Assert.AreEqual(expected, actual);
Assert.AreNotEqual(notExpected, actual);
Assert.AreSame(expectedObject, actualObject); // Reference equality
Assert.AreNotSame(notExpectedObject, actualObject);
// Null checks
Assert.IsNull(value);
Assert.IsNotNull(value);
// Boolean
Assert.IsTrue(condition);
Assert.IsFalse(condition);
// Fail/Inconclusive
Assert.Fail("Test failed due to...");
Assert.Inconclusive("Test cannot be completed because...");
```
### Exception Testing (Prefer over `[ExpectedException]`)
```csharp
// Assert.Throws - matches TException or derived types
var ex = Assert.Throws<ArgumentException>(() => Method(null));
Assert.AreEqual("Value cannot be null.", ex.Message);
// Assert.ThrowsExactly - matches exact type only
var ex = Assert.ThrowsExactly<InvalidOperationException>(() => Method());
// Async versions
var ex = await Assert.ThrowsAsync<HttpRequestException>(async () => await client.GetAsync(url));
var ex = await Assert.ThrowsExactlyAsync<InvalidOperationException>(async () => await Method());
```
### Collection Assertions (Assert class)
```csharp
Assert.Contains(expectedItem, collection);
Assert.DoesNotContain(unexpectedItem, collection);
Assert.ContainsSingle(collection); // exactly one element
Assert.HasCount(5, collection);
Assert.IsEmpty(collection);
Assert.IsNotEmpty(collection);
```
### String Assertions (Assert class)
```csharp
Assert.Contains("expected", actualString);
Assert.StartsWith("prefix", actualString);
Assert.EndsWith("suffix", actualString);
Assert.DoesNotStartWith("prefix", actualString);
Assert.DoesNotEndWith("suffix", actualString);
Assert.MatchesRegex(@"\d{3}-\d{4}", phoneNumber);
Assert.DoesNotMatchRegex(@"\d+", textOnly);
```
### Comparison Assertions
```csharp
Assert.IsGreaterThan(lowerBound, actual);
Assert.IsGreaterThanOrEqualTo(lowerBound, actual);
Assert.IsLessThan(upperBound, actual);
Assert.IsLessThanOrEqualTo(upperBound, actual);
Assert.IsInRange(actual, low, high);
Assert.IsPositive(number);
Assert.IsNegative(number);
```
### Type Assertions
```csharp
// MSTest 3.x - uses out parameter
Assert.IsInstanceOfType<MyClass>(obj, out var typed);
typed.DoSomething();
// MSTest 4.x - returns typed result directly
var typed = Assert.IsInstanceOfType<MyClass>(obj);
typed.DoSomething();
Assert.IsNotInstanceOfType<WrongType>(obj);
```
### Assert.That (MSTest 4.0+)
```csharp
Assert.That(result.Count > 0); // Auto-captures expression in failure message
```
### StringAssert Class
> **Note:** Prefer `Assert` class equivalents when available (e.g., `Assert.Contains("expected", actual)` over `StringAssert.Contains(actual, "expected")`).
```csharp
StringAssert.Contains(actualString, "expected");
StringAssert.StartsWith(actualString, "prefix");
StringAssert.EndsWith(actualString, "suffix");
StringAssert.Matches(actualString, new Regex(@"\d{3}-\d{4}"));
StringAssert.DoesNotMatch(actualString, new Regex(@"\d+"));
```
### CollectionAssert Class
> **Note:** Prefer `Assert` class equivalents when available (e.g., `Assert.Contains`).
```csharp
// Containment
CollectionAssert.Contains(collection, expectedItem);
CollectionAssert.DoesNotContain(collection, unexpectedItem);
// Equality (same elements, same order)
CollectionAssert.AreEqual(expectedCollection, actualCollection);
CollectionAssert.AreNotEqual(unexpectedCollection, actualCollection);
// Equivalence (same elements, any order)
CollectionAssert.AreEquivalent(expectedCollection, actualCollection);
CollectionAssert.AreNotEquivalent(unexpectedCollection, actualCollection);
// Subset checks
CollectionAssert.IsSubsetOf(subset, superset);
CollectionAssert.IsNotSubsetOf(notSubset, collection);
// Element validation
CollectionAssert.AllItemsAreInstancesOfType(collection, typeof(MyClass));
CollectionAssert.AllItemsAreNotNull(collection);
CollectionAssert.AllItemsAreUnique(collection);
```
## Data-Driven Tests
### DataRow
```csharp
[TestMethod]
[DataRow(1, 2, 3)]
[DataRow(0, 0, 0, DisplayName = "Zeros")]
[DataRow(-1, 1, 0, IgnoreMessage = "Known issue #123")] // MSTest 3.8+
public void Add_ReturnsSum(int a, int b, int expected)
{
Assert.AreEqual(expected, Calculator.Add(a, b));
}
```
### DynamicData
The data source can return any of the following types:
- `IEnumerable<(T1, T2, ...)>` (ValueTuple) - **preferred**, provides type safety (MSTest 3.7+)
- `IEnumerable<Tuple<T1, T2, ...>>` - provides type safety
- `IEnumerable<TestDataRow>` - provides type safety plus control over test metadata (display name, categories)
- `IEnumerable<object[]>` - **least preferred**, no type safety
> **Note:** When creating new test data methods, prefer `ValueTuple` or `TestDataRow` over `IEnumerable<object[]>`. The `object[]` approach provides no compile-time type checking and can lead to runtime errors from type mismatches.
```csharp
[TestMethod]
[DynamicData(nameof(TestData))]
public void DynamicTest(int a, int b, int expected)
{
Assert.AreEqual(expected, Calculator.Add(a, b));
}
// ValueTuple - preferred (MSTest 3.7+)
public static IEnumerable<(int a, int b, int expected)> TestData =>
[
(1, 2, 3),
(0, 0, 0),
];
// TestDataRow - when you need custom display names or metadata
public static IEnumerable<TestDataRow<(int a, int b, int expected)>> TestDataWithMetadata =>
[
new((1, 2, 3)) { DisplayName = "Positive numbers" },
new((0, 0, 0)) { DisplayName = "Zeros" },
new((-1, 1, 0)) { DisplayName = "Mixed signs", IgnoreMessage = "Known issue #123" },
];
// IEnumerable<object[]> - avoid for new code (no type safety)
public static IEnumerable<object[]> LegacyTestData =>
[
[1, 2, 3],
[0, 0, 0],
];
```
## TestContext
The `TestContext` class provides test run information, cancellation support, and output methods.
See [TestContext documentation](https://learn.microsoft.com/dotnet/core/testing/unit-testing-mstest-writing-tests-testcontext) for complete reference.
### Accessing TestContext
```csharp
// Property (MSTest suppresses CS8618 - don't use nullable or = null!)
public TestContext TestContext { get; set; }
// Constructor injection (MSTest 3.6+) - preferred for immutability
[TestClass]
public sealed class MyTests
{
private readonly TestContext _testContext;
public MyTests(TestContext testContext)
{
_testContext = testContext;
}
}
// Static methods receive it as parameter
[ClassInitialize]
public static void ClassInit(TestContext context) { }
// Optional for cleanup methods (MSTest 3.6+)
[ClassCleanup]
public static void ClassCleanup(TestContext context) { }
[AssemblyCleanup]
public static void AssemblyCleanup(TestContext context) { }
```
### Cancellation Token
Always use `TestContext.CancellationToken` for cooperative cancellation with `[Timeout]`:
```csharp
[TestMethod]
[Timeout(5000)]
public async Task LongRunningTest()
{
await _httpClient.GetAsync(url, TestContext.CancellationToken);
}
```
### Test Run Properties
```csharp
TestContext.TestName // Current test method name
TestContext.TestDisplayName // Display name (3.7+)
TestContext.CurrentTestOutcome // Pass/Fail/InProgress
TestContext.TestData // Parameterized test data (3.7+, in TestInitialize/Cleanup)
TestContext.TestException // Exception if test failed (3.7+, in TestCleanup)
TestContext.DeploymentDirectory // Directory with deployment items
```
### Output and Result Files
```csharp
// Write to test output (useful for debugging)
TestContext.WriteLine("Processing item {0}", itemId);
// Attach files to test results (logs, screenshots)
TestContext.AddResultFile(screenshotPath);
// Store/retrieve data across test methods
TestContext.Properties["SharedKey"] = computedValue;
```
## Advanced Features
### Retry for Flaky Tests (MSTest 3.9+)
```csharp
[TestMethod]
[Retry(3)]
public void FlakyTest() { }
```
### Conditional Execution (MSTest 3.10+)
Skip or run tests based on OS or CI environment:
```csharp
// OS-specific tests
[TestMethod]
[OSCondition(OperatingSystems.Windows)]
public void WindowsOnlyTest() { }
[TestMethod]
[OSCondition(OperatingSystems.Linux | OperatingSystems.MacOS)]
public void UnixOnlyTest() { }
[TestMethod]
[OSCondition(ConditionMode.Exclude, OperatingSystems.Windows)]
public void SkipOnWindowsTest() { }
// CI environment tests
[TestMethod]
[CICondition] // Runs only in CI (default: ConditionMode.Include)
public void CIOnlyTest() { }
[TestMethod]
[CICondition(ConditionMode.Exclude)] // Skips in CI, runs locally
public void LocalOnlyTest() { }
```
### Parallelization
```csharp
// Assembly level
[assembly: Parallelize(Workers = 4, Scope = ExecutionScope.MethodLevel)]
// Disable for specific class
[TestClass]
[DoNotParallelize]
public sealed class SequentialTests { }
```
### Work Item Traceability (MSTest 3.8+)
Link tests to work items for traceability in test reports:
```csharp
// Azure DevOps work items
[TestMethod]
[WorkItem(12345)] // Links to work item #12345
public void Feature_Scenario_ExpectedBehavior() { }
// Multiple work items
[TestMethod]
[WorkItem(12345)]
[WorkItem(67890)]
public void Feature_CoversMultipleRequirements() { }
// GitHub issues (MSTest 3.8+)
[TestMethod]
[GitHubWorkItem("https://github.com/owner/repo/issues/42")]
public void BugFix_Issue42_IsResolved() { }
```
Work item associations appear in test results and can be used for:
- Tracing test coverage to requirements
- Linking bug fixes to regression tests
- Generating traceability reports in CI/CD pipelines
## Common Mistakes to Avoid
```csharp
// ❌ Wrong argument order
Assert.AreEqual(actual, expected);
// ✅ Correct
Assert.AreEqual(expected, actual);
// ❌ Using ExpectedException (obsolete)
[ExpectedException(typeof(ArgumentException))]
// ✅ Use Assert.Throws
Assert.Throws<ArgumentException>(() => Method());
// ❌ Using LINQ Single() - unclear exception
var item = items.Single();
// ✅ Use ContainsSingle - better failure message
var item = Assert.ContainsSingle(items);
// ❌ Hard cast - unclear exception
var handler = (MyHandler)result;
// ✅ Type assertion - shows actual type on failure
var handler = Assert.IsInstanceOfType<MyHandler>(result);
// ❌ Ignoring cancellation token
await client.GetAsync(url, CancellationToken.None);
// ✅ Flow test cancellation
await client.GetAsync(url, TestContext.CancellationToken);
// ❌ Making TestContext nullable - leads to unnecessary null checks
public TestContext? TestContext { get; set; }
// ❌ Using null! - MSTest already suppresses CS8618 for this property
public TestContext TestContext { get; set; } = null!;
// ✅ Declare without nullable or initializer - MSTest handles the warning
public TestContext TestContext { get; set; }
```
## Test Organization
- Group tests by feature or component
- Use `[TestCategory("Category")]` for filtering
- Use `[TestProperty("Name", "Value")]` for custom metadata (e.g., `[TestProperty("Bug", "12345")]`)
- Use `[Priority(1)]` for critical tests
- Enable relevant MSTest analyzers (MSTEST0020 for constructor preference)
## Mocking and Isolation
- Use Moq or NSubstitute for mocking dependencies
- Use interfaces to facilitate mocking
- Mock dependencies to isolate units under test
@@ -0,0 +1,71 @@
---
name: csharp-nunit
description: 'Get best practices for NUnit unit testing, including data-driven tests'
---
# NUnit Best Practices
Your goal is to help me write effective unit tests with NUnit, covering both standard and data-driven testing approaches.
## Project Setup
- Use a separate test project with naming convention `[ProjectName].Tests`
- Reference Microsoft.NET.Test.Sdk, NUnit, and NUnit3TestAdapter packages
- Create test classes that match the classes being tested (e.g., `CalculatorTests` for `Calculator`)
- Use .NET SDK test commands: `dotnet test` for running tests
## Test Structure
- Apply `[TestFixture]` attribute to test classes
- Use `[Test]` attribute for test methods
- Follow the Arrange-Act-Assert (AAA) pattern
- Name tests using the pattern `MethodName_Scenario_ExpectedBehavior`
- Use `[SetUp]` and `[TearDown]` for per-test setup and teardown
- Use `[OneTimeSetUp]` and `[OneTimeTearDown]` for per-class setup and teardown
- Use `[SetUpFixture]` for assembly-level setup and teardown
## Standard Tests
- Keep tests focused on a single behavior
- Avoid testing multiple behaviors in one test method
- Use clear assertions that express intent
- Include only the assertions needed to verify the test case
- Make tests independent and idempotent (can run in any order)
- Avoid test interdependencies
## Data-Driven Tests
- Use `[TestCase]` for inline test data
- Use `[TestCaseSource]` for programmatically generated test data
- Use `[Values]` for simple parameter combinations
- Use `[ValueSource]` for property or method-based data sources
- Use `[Random]` for random numeric test values
- Use `[Range]` for sequential numeric test values
- Use `[Combinatorial]` or `[Pairwise]` for combining multiple parameters
## Assertions
- Use `Assert.That` with constraint model (preferred NUnit style)
- Use constraints like `Is.EqualTo`, `Is.SameAs`, `Contains.Item`
- Use `Assert.AreEqual` for simple value equality (classic style)
- Use `CollectionAssert` for collection comparisons
- Use `StringAssert` for string-specific assertions
- Use `Assert.Throws<T>` or `Assert.ThrowsAsync<T>` to test exceptions
- Use descriptive messages in assertions for clarity on failure
## Mocking and Isolation
- Consider using Moq or NSubstitute alongside NUnit
- Mock dependencies to isolate units under test
- Use interfaces to facilitate mocking
- Consider using a DI container for complex test setups
## Test Organization
- Group tests by feature or component
- Use categories with `[Category("CategoryName")]`
- Use `[Order]` to control test execution order when necessary
- Use `[Author("DeveloperName")]` to indicate ownership
- Use `[Description]` to provide additional test information
- Consider `[Explicit]` for tests that shouldn't run automatically
- Use `[Ignore("Reason")]` to temporarily skip tests
@@ -0,0 +1,100 @@
---
name: csharp-tunit
description: 'Get best practices for TUnit unit testing, including data-driven tests'
---
# TUnit Best Practices
Your goal is to help me write effective unit tests with TUnit, covering both standard and data-driven testing approaches.
## Project Setup
- Use a separate test project with naming convention `[ProjectName].Tests`
- Reference TUnit package and TUnit.Assertions for fluent assertions
- Create test classes that match the classes being tested (e.g., `CalculatorTests` for `Calculator`)
- Use .NET SDK test commands: `dotnet test` for running tests
- TUnit requires .NET 8.0 or higher
## Test Structure
- No test class attributes required (like xUnit/NUnit)
- Use `[Test]` attribute for test methods (not `[Fact]` like xUnit)
- Follow the Arrange-Act-Assert (AAA) pattern
- Name tests using the pattern `MethodName_Scenario_ExpectedBehavior`
- Use lifecycle hooks: `[Before(Test)]` for setup and `[After(Test)]` for teardown
- Use `[Before(Class)]` and `[After(Class)]` for shared context between tests in a class
- Use `[Before(Assembly)]` and `[After(Assembly)]` for shared context across test classes
- TUnit supports advanced lifecycle hooks like `[Before(TestSession)]` and `[After(TestSession)]`
## Standard Tests
- Keep tests focused on a single behavior
- Avoid testing multiple behaviors in one test method
- Use TUnit's fluent assertion syntax with `await Assert.That()`
- Include only the assertions needed to verify the test case
- Make tests independent and idempotent (can run in any order)
- Avoid test interdependencies (use `[DependsOn]` attribute if needed)
## Data-Driven Tests
- Use `[Arguments]` attribute for inline test data (equivalent to xUnit's `[InlineData]`)
- Use `[MethodData]` for method-based test data (equivalent to xUnit's `[MemberData]`)
- Use `[ClassData]` for class-based test data
- Create custom data sources by implementing `ITestDataSource`
- Use meaningful parameter names in data-driven tests
- Multiple `[Arguments]` attributes can be applied to the same test method
## Assertions
- Use `await Assert.That(value).IsEqualTo(expected)` for value equality
- Use `await Assert.That(value).IsSameReferenceAs(expected)` for reference equality
- Use `await Assert.That(value).IsTrue()` or `await Assert.That(value).IsFalse()` for boolean conditions
- Use `await Assert.That(collection).Contains(item)` or `await Assert.That(collection).DoesNotContain(item)` for collections
- Use `await Assert.That(value).Matches(pattern)` for regex pattern matching
- Use `await Assert.That(action).Throws<TException>()` or `await Assert.That(asyncAction).ThrowsAsync<TException>()` to test exceptions
- Chain assertions with `.And` operator: `await Assert.That(value).IsNotNull().And.IsEqualTo(expected)`
- Use `.Or` operator for alternative conditions: `await Assert.That(value).IsEqualTo(1).Or.IsEqualTo(2)`
- Use `.Within(tolerance)` for DateTime and numeric comparisons with tolerance
- All assertions are asynchronous and must be awaited
## Advanced Features
- Use `[Repeat(n)]` to repeat tests multiple times
- Use `[Retry(n)]` for automatic retry on failure
- Use `[ParallelLimit<T>]` to control parallel execution limits
- Use `[Skip("reason")]` to skip tests conditionally
- Use `[DependsOn(nameof(OtherTest))]` to create test dependencies
- Use `[Timeout(milliseconds)]` to set test timeouts
- Create custom attributes by extending TUnit's base attributes
## Test Organization
- Group tests by feature or component
- Use `[Category("CategoryName")]` for test categorization
- Use `[DisplayName("Custom Test Name")]` for custom test names
- Consider using `TestContext` for test diagnostics and information
- Use conditional attributes like custom `[WindowsOnly]` for platform-specific tests
## Performance and Parallel Execution
- TUnit runs tests in parallel by default (unlike xUnit which requires explicit configuration)
- Use `[NotInParallel]` to disable parallel execution for specific tests
- Use `[ParallelLimit<T>]` with custom limit classes to control concurrency
- Tests within the same class run sequentially by default
- Use `[Repeat(n)]` with `[ParallelLimit<T>]` for load testing scenarios
## Migration from xUnit
- Replace `[Fact]` with `[Test]`
- Replace `[Theory]` with `[Test]` and use `[Arguments]` for data
- Replace `[InlineData]` with `[Arguments]`
- Replace `[MemberData]` with `[MethodData]`
- Replace `Assert.Equal` with `await Assert.That(actual).IsEqualTo(expected)`
- Replace `Assert.True` with `await Assert.That(condition).IsTrue()`
- Replace `Assert.Throws<T>` with `await Assert.That(action).Throws<T>()`
- Replace constructor/IDisposable with `[Before(Test)]`/`[After(Test)]`
- Replace `IClassFixture<T>` with `[Before(Class)]`/`[After(Class)]`
**Why TUnit over xUnit?**
TUnit offers a modern, fast, and flexible testing experience with advanced features not present in xUnit, such as asynchronous assertions, more refined lifecycle hooks, and improved data-driven testing capabilities. TUnit's fluent assertions provide clearer and more expressive test validation, making it especially suitable for complex .NET projects.
@@ -0,0 +1,68 @@
---
name: csharp-xunit
description: 'Get best practices for XUnit unit testing, including data-driven tests'
---
# XUnit Best Practices
Your goal is to help me write effective unit tests with XUnit, covering both standard and data-driven testing approaches.
## Project Setup
- Use a separate test project with naming convention `[ProjectName].Tests`
- Reference Microsoft.NET.Test.Sdk, xunit, and xunit.runner.visualstudio packages
- Create test classes that match the classes being tested (e.g., `CalculatorTests` for `Calculator`)
- Use .NET SDK test commands: `dotnet test` for running tests
## Test Structure
- No test class attributes required (unlike MSTest/NUnit)
- Use fact-based tests with `[Fact]` attribute for simple tests
- Follow the Arrange-Act-Assert (AAA) pattern
- Name tests using the pattern `MethodName_Scenario_ExpectedBehavior`
- Use constructor for setup and `IDisposable.Dispose()` for teardown
- Use `IClassFixture<T>` for shared context between tests in a class
- Use `ICollectionFixture<T>` for shared context between multiple test classes
## Standard Tests
- Keep tests focused on a single behavior
- Avoid testing multiple behaviors in one test method
- Use clear assertions that express intent
- Include only the assertions needed to verify the test case
- Make tests independent and idempotent (can run in any order)
- Avoid test interdependencies
## Data-Driven Tests
- Use `[Theory]` combined with data source attributes
- Use `[InlineData]` for inline test data
- Use `[MemberData]` for method-based test data
- Use `[ClassData]` for class-based test data
- Create custom data attributes by implementing `DataAttribute`
- Use meaningful parameter names in data-driven tests
## Assertions
- Use `Assert.Equal` for value equality
- Use `Assert.Same` for reference equality
- Use `Assert.True`/`Assert.False` for boolean conditions
- Use `Assert.Contains`/`Assert.DoesNotContain` for collections
- Use `Assert.Matches`/`Assert.DoesNotMatch` for regex pattern matching
- Use `Assert.Throws<T>` or `await Assert.ThrowsAsync<T>` to test exceptions
- Use fluent assertions library for more readable assertions
## Mocking and Isolation
- Consider using Moq or NSubstitute alongside XUnit
- Mock dependencies to isolate units under test
- Use interfaces to facilitate mocking
- Consider using a DI container for complex test setups
## Test Organization
- Group tests by feature or component
- Use `[Trait("Category", "CategoryName")]` for categorization
- Use collection fixtures to group tests with shared dependencies
- Consider output helpers (`ITestOutputHelper`) for test diagnostics
- Skip tests conditionally with `Skip = "reason"` in fact/theory attributes
@@ -0,0 +1,85 @@
---
name: dotnet-best-practices
description: 'Ensure .NET/C# code meets best practices for the solution/project.'
---
# .NET/C# Best Practices
Your task is to ensure .NET/C# code in ${selection} meets the best practices specific to this solution/project. This includes:
## Documentation & Structure
- Create comprehensive XML documentation comments for all public classes, interfaces, methods, and properties
- Include parameter descriptions and return value descriptions in XML comments
- Follow the established namespace structure: {Core|Console|App|Service}.{Feature}
## Design Patterns & Architecture
- Use primary constructor syntax for dependency injection (e.g., `public class MyClass(IDependency dependency)`)
- Implement the Command Handler pattern with generic base classes (e.g., `CommandHandler<TOptions>`)
- Use interface segregation with clear naming conventions (prefix interfaces with 'I')
- Follow the Factory pattern for complex object creation.
## Dependency Injection & Services
- Use constructor dependency injection with null checks via ArgumentNullException
- Register services with appropriate lifetimes (Singleton, Scoped, Transient)
- Use Microsoft.Extensions.DependencyInjection patterns
- Implement service interfaces for testability
## Resource Management & Localization
- Use ResourceManager for localized messages and error strings
- Separate LogMessages and ErrorMessages resource files
- Access resources via `_resourceManager.GetString("MessageKey")`
## Async/Await Patterns
- Use async/await for all I/O operations and long-running tasks
- Return Task or Task<T> from async methods
- Use ConfigureAwait(false) where appropriate
- Handle async exceptions properly
## Testing Standards
- Use MSTest framework with FluentAssertions for assertions
- Follow AAA pattern (Arrange, Act, Assert)
- Use Moq for mocking dependencies
- Test both success and failure scenarios
- Include null parameter validation tests
## Configuration & Settings
- Use strongly-typed configuration classes with data annotations
- Implement validation attributes (Required, NotEmptyOrWhitespace)
- Use IConfiguration binding for settings
- Support appsettings.json configuration files
## Semantic Kernel & AI Integration
- Use Microsoft.SemanticKernel for AI operations
- Implement proper kernel configuration and service registration
- Handle AI model settings (ChatCompletion, Embedding, etc.)
- Use structured output patterns for reliable AI responses
## Error Handling & Logging
- Use structured logging with Microsoft.Extensions.Logging
- Include scoped logging with meaningful context
- Throw specific exceptions with descriptive messages
- Use try-catch blocks for expected failure scenarios
## Performance & Security
- Use C# 12+ features and .NET 8 optimizations where applicable
- Implement proper input validation and sanitization
- Use parameterized queries for database operations
- Follow secure coding practices for AI/ML operations
## Code Quality
- Ensure SOLID principles compliance
- Avoid code duplication through base classes and utilities
- Use meaningful names that reflect domain concepts
- Keep methods focused and cohesive
- Implement proper disposal patterns for resources
@@ -0,0 +1,42 @@
---
name: dotnet-design-pattern-review
description: 'Review the C#/.NET code for design pattern implementation and suggest improvements.'
---
# .NET/C# Design Pattern Review
Review the C#/.NET code in ${selection} for design pattern implementation and suggest improvements for the solution/project. Do not make any changes to the code, just provide a review.
## Required Design Patterns
- **Command Pattern**: Generic base classes (`CommandHandler<TOptions>`), `ICommandHandler<TOptions>` interface, `CommandHandlerOptions` inheritance, static `SetupCommand(IHost host)` methods
- **Factory Pattern**: Complex object creation service provider integration
- **Dependency Injection**: Primary constructor syntax, `ArgumentNullException` null checks, interface abstractions, proper service lifetimes
- **Repository Pattern**: Async data access interfaces provider abstractions for connections
- **Provider Pattern**: External service abstractions (database, AI), clear contracts, configuration handling
- **Resource Pattern**: ResourceManager for localized messages, separate .resx files (LogMessages, ErrorMessages)
## Review Checklist
- **Design Patterns**: Identify patterns used. Are Command Handler, Factory, Provider, and Repository patterns correctly implemented? Missing beneficial patterns?
- **Architecture**: Follow namespace conventions (`{Core|Console|App|Service}.{Feature}`)? Proper separation between Core/Console projects? Modular and readable?
- **.NET Best Practices**: Primary constructors, async/await with Task returns, ResourceManager usage, structured logging, strongly-typed configuration?
- **GoF Patterns**: Command, Factory, Template Method, Strategy patterns correctly implemented?
- **SOLID Principles**: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion violations?
- **Performance**: Proper async/await, resource disposal, ConfigureAwait(false), parallel processing opportunities?
- **Maintainability**: Clear separation of concerns, consistent error handling, proper configuration usage?
- **Testability**: Dependencies abstracted via interfaces, mockable components, async testability, AAA pattern compatibility?
- **Security**: Input validation, secure credential handling, parameterized queries, safe exception handling?
- **Documentation**: XML docs for public APIs, parameter/return descriptions, resource file organization?
- **Code Clarity**: Meaningful names reflecting domain concepts, clear intent through patterns, self-explanatory structure?
- **Clean Code**: Consistent style, appropriate method/class size, minimal complexity, eliminated duplication?
## Improvement Focus Areas
- **Command Handlers**: Validation in base class, consistent error handling, proper resource management
- **Factories**: Dependency configuration, service provider integration, disposal patterns
- **Providers**: Connection management, async patterns, exception handling and logging
- **Configuration**: Data annotations, validation attributes, secure sensitive value handling
- **AI/ML Integration**: Semantic Kernel patterns, structured output handling, model configuration
Provide specific, actionable recommendations for improvements aligned with the project's architecture and .NET best practices.
@@ -0,0 +1,116 @@
---
name: dotnet-upgrade
description: 'Ready-to-use prompts for comprehensive .NET framework upgrade analysis and execution'
---
# Project Discovery & Assessment
- name: "Project Classification Analysis"
prompt: "Identify all projects in the solution and classify them by type (`.NET Framework`, `.NET Core`, `.NET Standard`). Analyze each `.csproj` for its current `TargetFramework` and SDK usage."
- name: "Dependency Compatibility Review"
prompt: "Review external and internal dependencies for framework compatibility. Determine the upgrade complexity based on dependency graph depth."
- name: "Legacy Package Detection"
prompt: "Identify legacy `packages.config` projects needing migration to `PackageReference` format."
# Upgrade Strategy & Sequencing
- name: "Project Upgrade Ordering"
prompt: "Recommend a project upgrade order from least to most dependent components. Suggest how to isolate class library upgrades before API or Azure Function migrations."
- name: "Incremental Strategy Planning"
prompt: "Propose an incremental upgrade strategy with rollback checkpoints. Evaluate the use of **Upgrade Assistant** or **manual upgrades** based on project structure."
- name: "Progress Tracking Setup"
prompt: "Generate an upgrade checklist for tracking build, test, and deployment readiness across all projects."
# Framework Targeting & Code Adjustments
- name: "Target Framework Selection"
prompt: "Suggest the correct `TargetFramework` for each project (e.g., `net8.0`). Review and update deprecated SDK or build configurations."
- name: "Code Modernization Analysis"
prompt: "Identify code patterns needing modernization (e.g., `WebHostBuilder` → `HostBuilder`). Suggest replacements for deprecated .NET APIs and third-party libraries."
- name: "Async Pattern Conversion"
prompt: "Recommend conversion of synchronous calls to async where appropriate for improved performance and scalability."
# NuGet & Dependency Management
- name: "Package Compatibility Analysis"
prompt: "Analyze outdated or incompatible NuGet packages and suggest compatible versions. Identify third-party libraries that lack .NET 8 support and provide migration paths."
- name: "Shared Dependency Strategy"
prompt: "Recommend strategies for handling shared dependency upgrades across projects. Evaluate usage of legacy packages and suggest alternatives in Microsoft-supported namespaces."
- name: "Transitive Dependency Review"
prompt: "Review transitive dependencies and potential version conflicts after upgrade. Suggest resolution strategies for dependency conflicts."
# CI/CD & Build Pipeline Updates
- name: "Pipeline Configuration Analysis"
prompt: "Analyze YAML build definitions for SDK version pinning and recommend updates. Suggest modifications for `UseDotNet@2` and `NuGetToolInstaller` tasks."
- name: "Build Pipeline Modernization"
prompt: "Generate updated build pipeline snippets for .NET 8 migration. Recommend validation builds on feature branches before merging to main."
- name: "CI Automation Enhancement"
prompt: "Identify opportunities to automate test and build verification in CI pipelines. Suggest strategies for continuous integration validation."
# Testing & Validation
- name: "Build Validation Strategy"
prompt: "Propose validation checks to ensure the upgraded solution builds and runs successfully. Recommend automated test execution for unit and integration suites post-upgrade."
- name: "Service Integration Verification"
prompt: "Generate validation steps to verify logging, telemetry, and service connectivity. Suggest strategies for verifying backward compatibility and runtime behavior."
- name: "Deployment Readiness Check"
prompt: "Recommend UAT deployment verification steps before production rollout. Create comprehensive testing scenarios for upgraded components."
# Breaking Change Analysis
- name: "API Deprecation Detection"
prompt: "Identify deprecated APIs or removed namespaces between target versions. Suggest automated scanning using `.NET Upgrade Assistant` and API Analyzer."
- name: "API Replacement Strategy"
prompt: "Recommend replacement APIs or libraries for known breaking areas. Review configuration changes such as `Startup.cs` → `Program.cs` refactoring."
- name: "Regression Testing Focus"
prompt: "Suggest regression testing scenarios focused on upgraded API endpoints or services. Create test plans for critical functionality validation."
# Version Control & Commit Strategy
- name: "Branching Strategy Planning"
prompt: "Recommend branching strategy for safe upgrade with rollback capability. Generate commit templates for partial and complete project upgrades."
- name: "PR Structure Optimization"
prompt: "Suggest best practices for creating structured PRs (`Upgrade to .NET [Version]`). Identify tagging strategies for PRs involving breaking changes."
- name: "Code Review Guidelines"
prompt: "Recommend peer review focus areas (build, test, and dependency validation). Create checklists for effective upgrade reviews."
# Documentation & Communication
- name: "Upgrade Documentation Strategy"
prompt: "Suggest how to document each project's framework change in the PR. Propose automated release note generation summarizing upgrades and test results."
- name: "Stakeholder Communication"
prompt: "Recommend communicating version upgrades and migration timelines to consumers. Generate documentation templates for dependency updates and validation results."
- name: "Progress Tracking Systems"
prompt: "Suggest maintaining an upgrade summary dashboard or markdown checklist. Create templates for tracking upgrade progress across multiple projects."
# Tools & Automation
- name: "Upgrade Tool Selection"
prompt: "Recommend when and how to use: `.NET Upgrade Assistant`, `dotnet list package --outdated`, `dotnet migrate`, and `graph.json` dependency visualization."
- name: "Analysis Script Generation"
prompt: "Generate scripts or prompts for analyzing dependency graphs before upgrading. Propose AI-assisted prompts for Copilot to identify upgrade issues automatically."
- name: "Multi-Repository Validation"
prompt: "Suggest how to validate automation output across multiple repositories. Create standardized validation workflows for enterprise-scale upgrades."
# Final Validation & Delivery
- name: "Final Solution Validation"
prompt: "Generate validation steps to confirm the final upgraded solution passes all validation checks. Suggest production deployment verification steps post-upgrade."
- name: "Deployment Readiness Confirmation"
prompt: "Recommend generating final test results and build artifacts. Create a checklist summarizing completion across projects (builds/tests/deployment)."
- name: "Release Documentation"
prompt: "Generate a release note summarizing framework changes and CI/CD updates. Create comprehensive upgrade summary documentation."
---
@@ -0,0 +1,235 @@
---
name: minimal-api-file-upload
description: File upload endpoints in ASP.NET minimal APIs (.NET 8+)
license: MIT
---
# Implementing File Uploads in ASP.NET Core Minimal APIs
## When to Use
- File upload endpoints in ASP.NET Core minimal APIs (.NET 8+)
- Handling IFormFile or IFormFileCollection parameters
- When you need size limits, content type validation, or streaming large files
## When Not to Use
- MVC controllers → `[FromForm] IFormFile` works directly with attributes
- Simple JSON body → no file upload needed
- Very large files (> 1GB) → use streaming with `MultipartReader` instead
## Inputs
| Input | Required | Description |
|-------|----------|-------------|
| File parameter(s) | Yes | IFormFile or IFormFileCollection |
| Size limits | Yes | Max file/request size |
| Allowed types | No | Content type or extension restrictions |
## Workflow
### Step 1: CRITICAL — Understand IFormFile Binding in Minimal APIs
```csharp
// In .NET 8+ minimal APIs, IFormFile binds automatically from multipart/form-data
// when it is the only complex parameter.
app.MapPost("/upload", (IFormFile file) => ...);
// CRITICAL: When you mix files with other form fields, use [FromForm] on all
// form-bound parameters (or group them into a single [FromForm] DTO).
app.MapPost("/upload-with-metadata",
([FromForm] IFormFile file, [FromForm] string description) =>
{
return Results.Ok(new { file.FileName, Description = description });
});
// Multiple files: IFormFileCollection also binds automatically from multipart/form-data.
// You only need [FromForm] if you mix it with other form fields, as shown above.
app.MapPost("/upload-multiple", (IFormFileCollection files) =>
{
return Results.Ok(files.Select(f => new { f.FileName, f.Length }));
});
```
### Step 2: CRITICAL — File Size Limits Are Separate from Request Size Limits
```csharp
// CRITICAL: There are TWO different size limits and you need to configure BOTH
// 1. Request body size limit (Kestrel level) — default is 30MB
builder.WebHost.ConfigureKestrel(options =>
{
options.Limits.MaxRequestBodySize = 10 * 1024 * 1024; // 10 MB
});
// 2. Form options — multipart body length limit — default is 128MB
builder.Services.Configure<FormOptions>(options =>
{
options.MultipartBodyLengthLimit = 10 * 1024 * 1024; // 10 MB
options.ValueLengthLimit = 1024 * 1024; // 1 MB for form values
options.MultipartHeadersLengthLimit = 16384; // 16 KB for section headers
});
// COMMON MISTAKE: Only increasing Kestrel MaxRequestBodySize
// upload still fails because FormOptions.MultipartBodyLengthLimit is exceeded
// COMMON MISTAKE: Only increasing FormOptions
// upload fails with "Request body too large" from Kestrel before reaching form parsing
// CRITICAL: Per-endpoint override with RequestSizeLimit attribute
app.MapPost("/upload-large", [RequestSizeLimit(200_000_000)] (IFormFile file) =>
{
return Results.Ok(new { file.FileName, file.Length });
});
// CRITICAL: To disable the limit entirely (for streaming):
app.MapPost("/upload-unlimited", [DisableRequestSizeLimit] async (HttpContext context) =>
{
// Handle manually
});
```
### Step 3: CRITICAL — Anti-Forgery Auto-Validates Form Uploads in .NET 8+
```csharp
// CRITICAL: In .NET 8+ with UseAntiforgery(), ALL form-bound endpoints
// automatically validate anti-forgery tokens, INCLUDING file uploads
builder.Services.AddAntiforgery();
var app = builder.Build();
app.UseAntiforgery();
// This endpoint now REQUIRES an anti-forgery token:
app.MapPost("/upload", (IFormFile file) => Results.Ok(file.FileName));
// Without the token → 400 Bad Request
// CRITICAL: For API-only file uploads (no anti-forgery needed), opt out:
app.MapPost("/api/upload", (IFormFile file) => Results.Ok(file.FileName))
.DisableAntiforgery(); // CRITICAL: Must explicitly opt out
// COMMON MISTAKE: Getting 400 errors on file uploads and not realizing
// it's because UseAntiforgery() is in the pipeline
// WARNING: DisableAntiforgery() is safe for unauthenticated endpoints and
// endpoints using JWT bearer authentication. However, for endpoints
// authenticated with cookies, disabling antiforgery removes CSRF protection
// and exposes the endpoint to cross-site request forgery attacks.
// For cookie-authenticated endpoints, include a valid antiforgery token instead.
```
### Step 4: CRITICAL — Validate File Content, Not Just Extension
```csharp
app.MapPost("/upload", async (IFormFile file) =>
{
// CRITICAL: Check content type AND file signature (magic bytes)
// NEVER trust file extension alone — it can be spoofed
// Allow only JPEG/PNG by default. To support more (e.g., GIF),
// add the MIME type here AND validate its magic bytes below.
var allowedTypes = new[] { "image/jpeg", "image/png" };
if (!allowedTypes.Contains(file.ContentType, StringComparer.OrdinalIgnoreCase))
return Results.BadRequest("File type not allowed");
// CRITICAL: Check magic bytes for file type verification
using var stream = file.OpenReadStream();
var header = new byte[8];
var bytesRead = await stream.ReadAsync(header, 0, header.Length);
if (bytesRead < 4)
return Results.BadRequest("File content is too short or invalid");
// JPEG: FF D8 FF
// PNG: 89 50 4E 47
var isJpeg = header[0] == 0xFF && header[1] == 0xD8 && header[2] == 0xFF;
var isPng = header[0] == 0x89 && header[1] == 0x50 && header[2] == 0x4E && header[3] == 0x47;
// Determine the actual content type from magic bytes
string? detectedContentType = isJpeg ? "image/jpeg" : isPng ? "image/png" : null;
if (detectedContentType is null)
return Results.BadRequest("File content is not a supported image format (only JPEG and PNG are allowed).");
// Ensure the declared Content-Type matches what the magic bytes detected
if (!string.Equals(file.ContentType, detectedContentType, StringComparison.OrdinalIgnoreCase))
return Results.BadRequest("File content type does not match the declared ContentType header.");
// CRITICAL: Never use the user-provided filename directly for the save path — it can
// contain path traversal characters (e.g., "../../../etc/passwd").
// Generate a safe filename; derive the extension from validated content, not user input.
var extension = detectedContentType == "image/jpeg" ? ".jpg" : ".png";
var safeFileName = $"{Guid.NewGuid()}{extension}";
// NEVER: var path = Path.Combine("uploads", file.FileName); // Path traversal!
var filePath = Path.Combine("uploads", safeFileName);
Directory.CreateDirectory("uploads");
stream.Position = 0;
using var fileStream = File.Create(filePath);
await stream.CopyToAsync(fileStream);
return Results.Ok(new { FileName = safeFileName, file.Length });
});
```
### Step 5: CRITICAL — Streaming Large Files Without Buffering
```csharp
// CRITICAL: IFormFile relies on multipart form parsing that buffers content in memory
// (up to a threshold) then spills to temp files on disk. For very large uploads,
// this overhead is unnecessary if you can process the data in chunks.
// Use MultipartReader to stream directly — e.g., to a final storage location —
// without buffering the entire file first.
app.MapPost("/upload-stream",
[DisableRequestSizeLimit]
async (HttpContext context) =>
{
// Extract the multipart boundary from the Content-Type header
var contentType = context.Request.ContentType;
if (contentType == null)
return Results.BadRequest("Missing Content-Type");
// Safely parse the Content-Type header to avoid FormatException from MediaTypeHeaderValue.Parse
if (!MediaTypeHeaderValue.TryParse(contentType, out var mediaType))
return Results.BadRequest("Invalid Content-Type");
var boundary = HeaderUtilities.RemoveQuotes(mediaType.Boundary).Value;
if (string.IsNullOrWhiteSpace(boundary))
return Results.BadRequest("Not a multipart request");
var reader = new MultipartReader(boundary, context.Request.Body);
// CRITICAL: ReadNextSectionAsync returns null when there are no more sections
while (await reader.ReadNextSectionAsync() is { } section)
{
// Parse Content-Disposition to identify file sections
if (!ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition))
continue;
if (contentDisposition.DispositionType.Equals("form-data")
&& !string.IsNullOrEmpty(contentDisposition.FileName.Value))
{
// Sanitize the user-provided filename to prevent path traversal
var originalFileName = contentDisposition.FileName.Value ?? string.Empty;
var sanitizedFileName = Path.GetFileName(originalFileName.Trim('"'));
var safeFile = $"{Guid.NewGuid()}";
// CRITICAL: Stream directly to disk — avoids buffering in memory
Directory.CreateDirectory("uploads");
using var fileStream = File.Create(Path.Combine("uploads", safeFile));
await section.Body.CopyToAsync(fileStream);
}
}
return Results.Ok("Uploaded");
}).DisableAntiforgery();
// COMMON MISTAKE: Using IFormFile for very large files
// Multipart form parsing can buffer large uploads and consume memory/disk.
// Use MultipartReader for streaming directly to storage.
```
## Common Mistakes
1. **Only configuring one size limit**: Must configure BOTH Kestrel `MaxRequestBodySize` AND `FormOptions.MultipartBodyLengthLimit`.
2. **400 errors from anti-forgery**: In .NET 8+, `UseAntiforgery()` auto-validates form uploads. Use `.DisableAntiforgery()` for API endpoints (safe for JWT/unauthenticated; do NOT disable for cookie-authenticated endpoints).
3. **Trusting file.FileName**: User-provided filename can contain path traversal. Generate a safe filename with `Guid.NewGuid()` and derive the extension from validated content.
4. **Trusting Content-Type only**: Content type is client-spoofable. Always check magic bytes for actual file type verification.
5. **Using IFormFile for very large files**: Multipart form parsing buffers with a memory threshold and spills to temp files. Use `MultipartReader` to stream data in chunks directly to storage without buffering the entire file.
6. **Deriving file extension from user input**: Prefer deriving the extension from the validated content type or magic bytes rather than `Path.GetExtension(file.FileName)`. If the original extension must be preserved, validate it against the detected content type.