Generated: 2026-03-06
Status: Ready for Execution
Estimated Time: 3.5 hours
Total Commits: 13 (12 samples + 1 documentation)
This plan addresses 13 outdated samples identified in OUT-OF-DATE.md, migrating them to use modern .NET 10 APIs.
- Approach: Update in-place (replace old code with modern patterns)
- OpenAPI UI: Scalar UI (modern, clean, AOT-compatible)
- Scope: HIGH + MEDIUM priority items
- Git: One atomic commit per sample migration
| Priority | Category | Samples | Effort |
|---|---|---|---|
| HIGH | Swashbuckle → Built-in OpenAPI | 10 | 2 hours |
| HIGH | Manual SSE → Built-in SSE | 1 | 15 min |
| MEDIUM | NSwag → Built-in OpenAPI (MVC) | 1 | 15 min |
| MEDIUM | Custom IHostedService → BackgroundService | 1 | 15 min |
| FINAL | Documentation updates | 1 | 30 min |
- Read this file completely before executing any migrations
- Check git status:
git statusshould show clean working directory - Choose branch strategy:
# Option 1: Work on main (if you have push access) git checkout main git pull origin main # Option 2: Create feature branch (recommended) git checkout -b migrate-to-net10-patterns
Follow this order for best results:
-
Phase 1: OpenAPI Samples (10 samples, ~2 hours)
- Start with
open-api-1(establishes pattern) - Continue with
open-api-2, thenmap-group-*,map-4 - Then
pokedex,authentication-*samples - Finally
nswagandnswag-2
- Start with
-
Phase 2: SSE Sample (1 sample, ~15 min)
- Migrate
sseto useResults.ServerSentEvents()
- Migrate
-
Phase 3: IHostedService Sample (1 sample, ~15 min)
- Simplify
ihosted-service-1to useBackgroundService
- Simplify
-
Phase 4: Documentation (1 commit, ~30 min)
- Update all README files
- Mark OUT-OF-DATE.md as completed
Follow this workflow:
# 1. Navigate to sample
cd projects/<category>/<sample-name>
# 2. Read current implementation
cat Program.cs
cat <sample-name>.csproj
cat README.md
# 3. Make changes (see Migration Patterns below)
# - Update .csproj
# - Update Program.cs
# - Update README.md
# 4. Test thoroughly
dotnet build
dotnet watch run
# Test in browser (see Testing Checklist)
# 5. Stop the app (Ctrl+C)
# 6. Return to root
cd /mnt/d/GitHub/practical-aspnetcore
# 7. Review changes
git status
git diff projects/<category>/<sample-name>/
# 8. Stage changes
git add projects/<category>/<sample-name>/
# 9. Commit with message (see Commit Messages below)
git commit -m "..."
# 10. Verify commit
git log -1 --stat
# 11. Repeat for next sampleApplies to: open-api-1, open-api-2, map-group-2, map-group-3, map-4, pokedex, authentication-4, authentication-5, nswag, nswag-2
Location: projects/<category>/<sample-name>/<sample-name>.csproj
Remove:
<PackageReference Include="Swashbuckle.AspNetCore" Version="..." />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.x" />
<PackageReference Include="NSwag.AspNetCore" Version="..." />Add:
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>true</ImplicitUsings>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0-preview.5.*" />
<PackageReference Include="Scalar.AspNetCore" Version="2.1.13" />
</ItemGroup>Location: projects/<category>/<sample-name>/Program.cs
Add at top:
using Scalar.AspNetCore;Remove:
// These using statements
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi.Models;
// Service registration
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(setup => setup.SwaggerDoc("v1", new OpenApiInfo() { ... }));
// Middleware
app.UseSwagger();
app.UseSwaggerUI();
// For NSwag samples
builder.Services.AddSwaggerDocument(settings => { ... });
app.UseOpenApi();
app.UseSwaggerUi(settings => { ... });
// Extension method on endpoints
.WithOpenApi(op =>
{
op.OperationId = "...";
op.Summary = "...";
return op;
});Add:
// Service registration (after other services)
builder.Services.AddOpenApi();
// Middleware (after app.Build(), before endpoints)
app.MapOpenApi();
app.MapScalarApiReference();Replace .WithOpenApi() with XML comments:
// BEFORE
app.MapGet("/greeting", Hello.GetGreeting).WithOpenApi(op =>
{
op.OperationId = "GetGreetings";
op.Summary = "Return greeting given name";
return op;
});
// AFTER
/// <summary>
/// Return greeting given name
/// </summary>
/// <param name="name">The name of the person to greet</param>
app.MapGet("/greeting", Hello.GetGreeting);For MVC controllers (NSwag samples):
// Add XML comments to controller actions:
/// <summary>
/// Returns a hello world message
/// </summary>
/// <response code="200">The greeting message</response>
[HttpGet("")]
public ActionResult<Greeting> Index()
{
return new Greeting { Message = "Hello World" };
}Location: projects/<category>/<sample-name>/README.md
Replace content with:
# Built-in OpenAPI with Scalar UI
This sample demonstrates ASP.NET Core 10's built-in OpenAPI 3.1 support.
## Key Features
- No external packages required (Swashbuckle/NSwag removed)
- OpenAPI 3.1 document generated automatically
- XML doc comments populate API descriptions
- Modern Scalar UI for interactive documentation
- AOT-compatible
## Running the Sample
```bash
dotnet watch run- Scalar UI: Navigate to
/scalar - OpenAPI JSON: Navigate to
/openapi/v1.json
This sample was migrated from Swashbuckle to .NET 10's built-in OpenAPI support.
Changes:
- Removed
Swashbuckle.AspNetCorepackage dependency - Added
Microsoft.AspNetCore.OpenApi(built-in) - Added
Scalar.AspNetCorefor modern UI - Replaced
WithOpenApi()with XML documentation comments - Enabled
GenerateDocumentationFilein .csproj
See OUT-OF-DATE.md for migration details.
**For NSwag samples, adjust to mention NSwag:**
```markdown
# Built-in OpenAPI with MVC Controllers
This sample demonstrates ASP.NET Core 10's built-in OpenAPI support with MVC controllers.
## Migration Notes
This sample was migrated from NSwag to .NET 10's built-in OpenAPI support.
Applies to: sse
Location: projects/sse/Program.cs
Add at top:
using System.Runtime.CompilerServices;Replace the entire /sse endpoint (lines 6-38) with:
app.MapGet("/sse", (HttpContext context, CancellationToken cancellationToken) =>
{
async IAsyncEnumerable<string> CounterAsync([EnumeratorCancellation] CancellationToken ct)
{
int count = 0;
while (!ct.IsCancellationRequested)
{
yield return $"hello world {++count}";
await Task.Delay(3000, ct);
}
}
if (context.Request.Headers.Accept == "text/event-stream")
{
return Results.ServerSentEvents(CounterAsync(cancellationToken), eventType: "message");
}
else
{
return Results.BadRequest("Unsupported Accept header. Use 'text/event-stream'.");
}
});Remove the Counter() method at the bottom (lines 69-76) - no longer needed
Location: projects/sse/README.md
Replace content with:
# Built-in Server-Sent Events
This sample demonstrates ASP.NET Core 10's built-in Server-Sent Events (SSE) support using `Results.ServerSentEvents()`.
## Running the Sample
```bash
dotnet watch runNavigate to http://localhost:5000/ to see the SSE client in action.
- Built-in SSE support via
Results.ServerSentEvents() - Type-safe with
IAsyncEnumerable<T> - Automatic flush management
- Proper cancellation token handling
- No manual protocol implementation needed
The endpoint returns Results.ServerSentEvents() with an IAsyncEnumerable<string>:
app.MapGet("/sse", (HttpContext context, CancellationToken cancellationToken) =>
{
async IAsyncEnumerable<string> CounterAsync([EnumeratorCancellation] CancellationToken ct)
{
int count = 0;
while (!ct.IsCancellationRequested)
{
yield return $"hello world {++count}";
await Task.Delay(3000, ct);
}
}
if (context.Request.Headers.Accept == "text/event-stream")
{
return Results.ServerSentEvents(CounterAsync(cancellationToken), eventType: "message");
}
return Results.BadRequest("Use Accept: text/event-stream");
});This sample was migrated from manual SSE implementation to .NET 10's built-in support.
Changes:
- Removed manual SSE protocol implementation (data/id/event formatting)
- Removed manual
Response.WriteAsync()andFlushAsync()calls - Added
Results.ServerSentEvents()withIAsyncEnumerable<string> - Added proper cancellation token handling with
[EnumeratorCancellation]
See OUT-OF-DATE.md for migration details.
projects/net10/sse-2/- Basic SSEprojects/net10/sse-3/- SSE with event typesprojects/net10/sse-4/- SSE with mixed events usingSseItem<T>
---
### Pattern C: IHostedService Migration (Custom → BackgroundService)
**Applies to:** `ihosted-service-1`
#### Step 1: Update Program.cs
**Location:** `projects/ihosted-service/ihosted-service-1/Program.cs`
**Add at top:**
```csharp
using Microsoft.Extensions.Hosting;
Remove the entire HostedService abstract class (lines 15-51)
Replace the GreeterUpdaterService class (lines 53-69) with:
public class GreeterUpdaterService : BackgroundService
{
private readonly Greeter _greeter;
public GreeterUpdaterService(Greeter greeter)
{
_greeter = greeter;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_greeter.Counter++;
await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken);
}
}
}Update the service registration (line 3):
// BEFORE
builder.Services.AddSingleton<Microsoft.Extensions.Hosting.IHostedService, GreeterUpdaterService>();
// AFTER
builder.Services.AddHostedService<GreeterUpdaterService>();Location: projects/ihosted-service/ihosted-service-1/README.md
Replace content with:
# BackgroundService Pattern
This sample demonstrates using the `BackgroundService` base class for background tasks in ASP.NET Core.
## Running the Sample
```bash
dotnet watch runNavigate to http://localhost:5000/ and reload the page to see the counter incrementing.
A GreeterUpdaterService inherits from BackgroundService and updates a Greeter singleton every second:
public class GreeterUpdaterService : BackgroundService
{
private readonly Greeter _greeter;
public GreeterUpdaterService(Greeter greeter)
{
_greeter = greeter;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_greeter.Counter++;
await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken);
}
}
}builder.Services.AddSingleton<Greeter>();
builder.Services.AddHostedService<GreeterUpdaterService>();This sample was simplified from a custom IHostedService implementation to using the built-in BackgroundService base class.
Changes:
- Removed custom
HostedServiceabstract base class (40+ lines of boilerplate) - Inherited from
Microsoft.Extensions.Hosting.BackgroundService - Simplified cancellation token handling
- Cleaner, more maintainable code
Benefits:
- Much less code to maintain
BackgroundServicehandles all the boilerplate- Built into
Microsoft.Extensions.Hosting - Standard pattern recommended by Microsoft
See OUT-OF-DATE.md for migration details.
---
## Testing Checklist
### For OpenAPI Samples
**Before each commit, verify:**
```bash
# 1. Build succeeds
cd projects/<category>/<sample-name>
dotnet build
# Should see: Build succeeded. 0 Warning(s). 0 Error(s).
# 2. Run succeeds
dotnet watch run
# Should see: Now listening on: http://localhost:5000
# 3. Test in browser
# Open browser to http://localhost:5000/scalar
# - Scalar UI should load
# - Endpoints should be listed
# - Click on endpoint, should see parameters and descriptions
# 4. Test OpenAPI document
# Navigate to http://localhost:5000/openapi/v1.json
# - Should see valid JSON
# - Should include endpoint descriptions from XML comments
# 5. Stop the app
# Press Ctrl+C in terminal
# 6. Return to root
cd /mnt/d/GitHub/practical-aspnetcore
Common issues:
- Error: "XML documentation file not found" → Check
GenerateDocumentationFilein .csproj - Error: "Scalar UI not found" → Check
Scalar.AspNetCorepackage is installed - Error: "OpenAPI document empty" → Check
builder.Services.AddOpenApi()is called
# 1. Build and run
cd projects/sse
dotnet build
dotnet watch run
# 2. Test in browser
# Navigate to http://localhost:5000/
# - Page should load with empty list
# - Open browser console (F12)
# - Should see: "Connecting to SSE..."
# - Should see: "Connection opened:"
# - List items should appear every 3 seconds
# - Each item should say "hello world X"
# 3. Stop the app
# Press Ctrl+C
# 4. Return to root
cd /mnt/d/GitHub/practical-aspnetcore# 1. Build and run
cd projects/ihosted-service/ihosted-service-1
dotnet build
dotnet watch run
# 2. Test in browser
# Navigate to http://localhost:5000/
# - Should see: "Please reload page (greeting updated every 1 second in the background) Hello world 0"
# - Reload the page
# - Counter should have incremented
# - Example: "Hello world 5" (if 5 seconds passed)
# 3. Stop the app
# Press Ctrl+C
# 4. Return to root
cd /mnt/d/GitHub/practical-aspnetcore<action> <sample-path> to .NET 10 <feature>
- <change 1>
- <change 2>
- <change 3>
Refs: OUT-OF-DATE.md - <category>
git commit -m "Migrate minimal-api/open-api-1 to .NET 10 built-in OpenAPI
- Replace Swashbuckle.AspNetCore with Microsoft.AspNetCore.OpenApi 10.0.0-preview
- Add Scalar.AspNetCore for modern API documentation UI
- Remove deprecated WithOpenApi() extension method
- Add XML doc comments for endpoint descriptions
- Update .csproj: enable GenerateDocumentationFile
- Update README.md with built-in OpenAPI notes
Refs: OUT-OF-DATE.md - Category 1: Swashbuckle/OpenAPI Samples"git commit -m "Migrate minimal-api/open-api-2 to .NET 10 built-in OpenAPI
- Replace Swashbuckle.AspNetCore with Microsoft.AspNetCore.OpenApi 10.0.0-preview
- Add Scalar.AspNetCore for modern API documentation UI
- Remove deprecated WithOpenApi() extension method
- Add XML doc comments for endpoint descriptions and responses
- Update .csproj: enable GenerateDocumentationFile
- Update README.md with built-in OpenAPI notes
Refs: OUT-OF-DATE.md - Category 1: Swashbuckle/OpenAPI Samples"git commit -m "Migrate minimal-api/map-group-2 to .NET 10 built-in OpenAPI
- Replace Swashbuckle.AspNetCore with Microsoft.AspNetCore.OpenApi 10.0.0-preview
- Add Scalar.AspNetCore for modern API documentation UI
- Update .csproj: enable GenerateDocumentationFile
- Update README.md with built-in OpenAPI notes
Refs: OUT-OF-DATE.md - Category 1: Swashbuckle/OpenAPI Samples"git commit -m "Migrate minimal-api/map-group-3 to .NET 10 built-in OpenAPI
- Replace Swashbuckle.AspNetCore with Microsoft.AspNetCore.OpenApi 10.0.0-preview
- Add Scalar.AspNetCore for modern API documentation UI
- Update .csproj: enable GenerateDocumentationFile
- Update README.md with built-in OpenAPI notes
Refs: OUT-OF-DATE.md - Category 1: Swashbuckle/OpenAPI Samples"git commit -m "Migrate minimal-api/map-4 to .NET 10 built-in OpenAPI
- Replace Swashbuckle.AspNetCore with Microsoft.AspNetCore.OpenApi 10.0.0-preview
- Add Scalar.AspNetCore for modern API documentation UI
- Update .csproj: enable GenerateDocumentationFile
- Update README.md with built-in OpenAPI notes
Refs: OUT-OF-DATE.md - Category 1: Swashbuckle/OpenAPI Samples"git commit -m "Migrate pokedex to .NET 10 built-in OpenAPI
- Replace Swashbuckle.AspNetCore with Microsoft.AspNetCore.OpenApi 10.0.0-preview
- Add Scalar.AspNetCore for modern API documentation UI
- Update .csproj: enable GenerateDocumentationFile
- Update README.md with built-in OpenAPI notes
Refs: OUT-OF-DATE.md - Category 1: Swashbuckle/OpenAPI Samples"git commit -m "Migrate authentication-4 to .NET 10 built-in OpenAPI
- Replace Swashbuckle.AspNetCore with Microsoft.AspNetCore.OpenApi 10.0.0-preview
- Add Scalar.AspNetCore for modern API documentation UI
- Update .csproj: enable GenerateDocumentationFile
- Update README.md with built-in OpenAPI notes
Refs: OUT-OF-DATE.md - Category 1: Swashbuckle/OpenAPI Samples"git commit -m "Migrate authentication-5 to .NET 10 built-in OpenAPI
- Replace Swashbuckle.AspNetCore with Microsoft.AspNetCore.OpenApi 10.0.0-preview
- Add Scalar.AspNetCore for modern API documentation UI
- Remove deprecated WithOpenApi() extension method
- Add XML doc comments for endpoint descriptions
- Update .csproj: enable GenerateDocumentationFile
- Update README.md with built-in OpenAPI notes
Refs: OUT-OF-DATE.md - Category 1: Swashbuckle/OpenAPI Samples"git commit -m "Migrate mvc/nswag to .NET 10 built-in OpenAPI with MVC
- Replace NSwag.AspNetCore with Microsoft.AspNetCore.OpenApi 10.0.0-preview
- Add Scalar.AspNetCore for modern API documentation UI
- Add XML doc comments to controller actions
- Update .csproj: enable GenerateDocumentationFile
- Update README.md with built-in OpenAPI notes
Refs: OUT-OF-DATE.md - Category 1: Swashbuckle/OpenAPI Samples"git commit -m "Migrate mvc/nswag-2 to .NET 10 built-in OpenAPI with MVC
- Replace NSwag.AspNetCore with Microsoft.AspNetCore.OpenApi 10.0.0-preview
- Add Scalar.AspNetCore for modern API documentation UI
- Add XML doc comments to controller actions
- Update .csproj: enable GenerateDocumentationFile
- Update README.md with built-in OpenAPI notes
Refs: OUT-OF-DATE.md - Category 1: Swashbuckle/OpenAPI Samples"git commit -m "Migrate sse sample to .NET 10 built-in Server-Sent Events
- Replace manual SSE protocol implementation with Results.ServerSentEvents()
- Use IAsyncEnumerable<string> with proper cancellation token handling
- Add [EnumeratorCancellation] attribute for proper cancellation
- Remove manual Response.WriteAsync() and FlushAsync() calls
- Simplify code with automatic flush management
- Update README.md with built-in SSE notes
Refs: OUT-OF-DATE.md - Category 2: Server-Sent Events"git commit -m "Simplify ihosted-service-1 using BackgroundService base class
- Remove custom HostedService abstract base class (40+ lines of boilerplate)
- Inherit from Microsoft.Extensions.Hosting.BackgroundService
- Update service registration to use AddHostedService<T>()
- Cleaner cancellation token handling via stoppingToken parameter
- Update README.md with BackgroundService pattern notes
Refs: OUT-OF-DATE.md - Category 3: IHostedService Patterns"git commit -m "Update documentation to reflect .NET 10 migrations
- Update root README.md with note about .NET 10 modern patterns
- Update projects/minimal-api/README.md with OpenAPI migration notes
- Update projects/authentication/README.md with OpenAPI migration notes
- Update projects/sse/README.md with built-in SSE notes
- Update projects/mvc/README.md with NSwag alternative notes
- Update projects/ihosted-service/README.md with BackgroundService pattern notes
- Mark OUT-OF-DATE.md as completed
Refs: OUT-OF-DATE.md"Location: projects/minimal-api/open-api-1/
Files to update:
open-api-1.csprojProgram.csREADME.md
Steps:
cd projects/minimal-api/open-api-1
# Read current files
cat open-api-1.csproj
cat Program.cs
cat README.md
# Update .csproj (see Pattern A, Step 1)
# Update Program.cs (see Pattern A, Step 2)
# Update README.md (see Pattern A, Step 3)
# Test
dotnet build
dotnet watch run
# Test in browser: /scalar and /openapi/v1.json
# Press Ctrl+C to stop
cd /mnt/d/GitHub/practical-aspnetcore
# Commit
git add projects/minimal-api/open-api-1/
git commit -m "Migrate minimal-api/open-api-1 to .NET 10 built-in OpenAPI
- Replace Swashbuckle.AspNetCore with Microsoft.AspNetCore.OpenApi 10.0.0-preview
- Add Scalar.AspNetCore for modern API documentation UI
- Remove deprecated WithOpenApi() extension method
- Add XML doc comments for endpoint descriptions
- Update .csproj: enable GenerateDocumentationFile
- Update README.md with built-in OpenAPI notes
Refs: OUT-OF-DATE.md - Category 1: Swashbuckle/OpenAPI Samples"
# Verify
git log -1 --statLocation: projects/minimal-api/open-api-2/
Steps: Same as Sample 1, but use commit message for open-api-2
Location: projects/minimal-api/map-group-2/
Steps: Same as Sample 1, but use commit message for map-group-2
Location: projects/minimal-api/map-group-3/
Steps: Same as Sample 1, but use commit message for map-group-3
Location: projects/minimal-api/map-4/
Steps: Same as Sample 1, but use commit message for map-4
Location: projects/mini/minimal-api-pokedex/src/Minimal.Api.Pokedex/
Note: This is a multi-project sample. Update the main API project.
Steps:
cd projects/mini/minimal-api-pokedex/src/Minimal.Api.Pokedex
# Read current files
cat Minimal.Api.Pokedex.csproj
cat Program.cs
# Update .csproj and Program.cs using Pattern A
# Test
dotnet build
dotnet watch run
# Test in browser
# Press Ctrl+C
cd /mnt/d/GitHub/practical-aspnetcore
# Commit
git add projects/mini/minimal-api-pokedex/
git commit -m "Migrate pokedex to .NET 10 built-in OpenAPI
- Replace Swashbuckle.AspNetCore with Microsoft.AspNetCore.OpenApi 10.0.0-preview
- Add Scalar.AspNetCore for modern API documentation UI
- Update .csproj: enable GenerateDocumentationFile
- Update README.md with built-in OpenAPI notes
Refs: OUT-OF-DATE.md - Category 1: Swashbuckle/OpenAPI Samples"Location: projects/authentication/authentication-4/
Steps: Same as Sample 1, but use commit message for authentication-4
Location: projects/authentication/authentication-5/
Steps: Same as Sample 1, but use commit message for authentication-5
Location: projects/mvc/nswag/
Steps:
cd projects/mvc/nswag
# Read current files
cat nswag.csproj
cat Program.cs
# Update .csproj (see Pattern A, Step 1 - remove NSwag, add Scalar)
# Update Program.cs (see Pattern A, Step 2 - for MVC)
# Add XML comments to controller actions
# Test
dotnet build
dotnet watch run
# Test in browser: /scalar
# Press Ctrl+C
cd /mnt/d/GitHub/practical-aspnetcore
# Commit
git add projects/mvc/nswag/
git commit -m "Migrate mvc/nswag to .NET 10 built-in OpenAPI with MVC
- Replace NSwag.AspNetCore with Microsoft.AspNetCore.OpenApi 10.0.0-preview
- Add Scalar.AspNetCore for modern API documentation UI
- Add XML doc comments to controller actions
- Update .csproj: enable GenerateDocumentationFile
- Update README.md with built-in OpenAPI notes
Refs: OUT-OF-DATE.md - Category 1: Swashbuckle/OpenAPI Samples"Location: projects/mvc/nswag-2/
Steps: Same as Sample 9, but use commit message for nswag-2
Location: projects/sse/
Steps:
cd projects/sse
# Read current file
cat Program.cs
# Update Program.cs (see Pattern B, Step 1)
# Update README.md (see Pattern B, Step 2)
# Test
dotnet build
dotnet watch run
# Test in browser: http://localhost:5000/
# Open console, verify SSE events
# Press Ctrl+C
cd /mnt/d/GitHub/practical-aspnetcore
# Commit
git add projects/sse/
git commit -m "Migrate sse sample to .NET 10 built-in Server-Sent Events
- Replace manual SSE protocol implementation with Results.ServerSentEvents()
- Use IAsyncEnumerable<string> with proper cancellation token handling
- Add [EnumeratorCancellation] attribute for proper cancellation
- Remove manual Response.WriteAsync() and FlushAsync() calls
- Simplify code with automatic flush management
- Update README.md with built-in SSE notes
Refs: OUT-OF-DATE.md - Category 2: Server-Sent Events"Location: projects/ihosted-service/ihosted-service-1/
Steps:
cd projects/ihosted-service/ihosted-service-1
# Read current file
cat Program.cs
# Update Program.cs (see Pattern C, Step 1)
# Update README.md (see Pattern C, Step 2)
# Test
dotnet build
dotnet watch run
# Test in browser: http://localhost:5000/
# Reload page, counter should increment
# Press Ctrl+C
cd /mnt/d/GitHub/practical-aspnetcore
# Commit
git add projects/ihosted-service/ihosted-service-1/
git commit -m "Simplify ihosted-service-1 using BackgroundService base class
- Remove custom HostedService abstract base class (40+ lines of boilerplate)
- Inherit from Microsoft.Extensions.Hosting.BackgroundService
- Update service registration to use AddHostedService<T>()
- Cleaner cancellation token handling via stoppingToken parameter
- Update README.md with BackgroundService pattern notes
Refs: OUT-OF-DATE.md - Category 3: IHostedService Patterns"Location: Root and category READMEs
Steps:
# Update root README.md
# Add a section about .NET 10 modern patterns after the introduction
# Update category READMEs:
# - projects/minimal-api/README.md
# - projects/authentication/README.md
# - projects/sse/README.md
# - projects/mvc/README.md
# - projects/ihosted-service/README.md
# For each category README, add a note:
# "Note: Samples have been migrated to use .NET 10 built-in features. See MIGRATION-PLAN.md for details."
# Update or remove OUT-OF-DATE.md
# Option 1: Add at top: "STATUS: MIGRATION COMPLETED (2026-03-06)"
# Option 2: Delete the file
# Commit
git add README.md
git add projects/*/README.md
git add OUT-OF-DATE.md
git commit -m "Update documentation to reflect .NET 10 migrations
- Update root README.md with note about .NET 10 modern patterns
- Update projects/minimal-api/README.md with OpenAPI migration notes
- Update projects/authentication/README.md with OpenAPI migration notes
- Update projects/sse/README.md with built-in SSE notes
- Update projects/mvc/README.md with NSwag alternative notes
- Update projects/ihosted-service/README.md with BackgroundService pattern notes
- Mark OUT-OF-DATE.md as completed
Refs: OUT-OF-DATE.md"# 1. Check commit history
git log --oneline -13
# Should see all 13 commits in reverse chronological order
# 2. Verify no uncommitted changes
git status
# Should say: "nothing to commit, working tree clean"
# 3. Optional: Build all migrated samples
cd projects/minimal-api/open-api-1 && dotnet build && cd ../../..
cd projects/minimal-api/open-api-2 && dotnet build && cd ../../..
cd projects/minimal-api/map-group-2 && dotnet build && cd ../../..
cd projects/minimal-api/map-group-3 && dotnet build && cd ../../..
cd projects/minimal-api/map-4 && dotnet build && cd ../../..
cd projects/mini/minimal-api-pokedex/src/Minimal.Api.Pokedex && dotnet build && cd ../../../../..
cd projects/authentication/authentication-4 && dotnet build && cd ../../..
cd projects/authentication/authentication-5 && dotnet build && cd ../../..
cd projects/mvc/nswag && dotnet build && cd ../..
cd projects/mvc/nswag-2 && dotnet build && cd ../..
cd projects/sse && dotnet build && cd ../..
cd projects/ihosted-service/ihosted-service-1 && dotnet build && cd ../../..
# 4. If using feature branch, push to remote
git push origin migrate-to-net10-patterns
# 5. If ready to merge to main
git checkout main
git merge migrate-to-net10-patterns
git push origin mainBefore marking this migration as complete, verify:
- All 13 commits created with descriptive messages
- Each commit is atomic (all files for that sample in one commit)
- All 12 samples build without warnings (
dotnet buildsucceeds) - All 12 samples run correctly (
dotnet watch runsucceeds) - OpenAPI samples:
/scalarand/openapi/v1.jsonaccessible (10 samples) - SSE sample: Events received in browser every 3 seconds
- IHostedService sample: Counter increments every second
- No deprecated
WithOpenApi()usage remains - No
Swashbuckle.AspNetCoreorNSwag.AspNetCorepackages remain - All READMEs updated with migration notes
- Git history shows clear, traceable progression
-
git log --oneline -13shows all commits -
git statusshows clean working tree - Root README and category READMEs updated in final commit
- OUT-OF-DATE.md marked as completed or removed
Solution: Add using Microsoft.Extensions.Hosting; at top of Program.cs
Solution: Add to .csproj:
<GenerateDocumentationFile>true</GenerateDocumentationFile>Solution: Check that:
Scalar.AspNetCorepackage is installedapp.MapScalarApiReference();is called afterapp.Build()- Navigate to
/scalar(not/swagger)
Solution: Check that:
builder.Services.AddOpenApi();is calledapp.MapOpenApi();is called- At least one endpoint is mapped
Solution: Check that:
using System.Runtime.CompilerServices;is added[EnumeratorCancellation]attribute is on the cancellation token parameter- Browser is sending
Accept: text/event-streamheader
Solution:
- Check .csproj for correct package versions
- Run
dotnet restore - Delete
bin/andobj/folders and rebuild
- .NET 10 What's New
- ASP.NET Core 10 What's New
- Built-in OpenAPI Support
- Server-Sent Events
- BackgroundService Class
OUT-OF-DATE.md- Original analysis of outdated samplesprojects/net10/README.md- .NET 10 feature samples indexprojects/net10/open-api-8/- Reference for built-in OpenAPI patternprojects/net10/sse-2/- Reference for built-in SSE patternAGENTS.md- Repository conventions and guidelines
-
This plan is self-contained - All information needed to execute the migration is in this file.
-
Follow the order - Execute samples in the order listed (1-13) for best results.
-
Test before commit - Always run
dotnet buildanddotnet watch runbefore committing. -
Use the exact commit messages - Copy-paste the commit messages to ensure consistency.
-
One sample at a time - Don't try to batch multiple samples in one commit.
-
Reference existing .NET 10 samples - If unsure, look at
projects/net10/open-api-8/orprojects/net10/sse-2/for patterns. -
Keep it simple - The goal is to demonstrate modern patterns with minimal code.
-
Update READMEs - Don't skip updating README.md files - they help users understand the changes.
-
Mark complete - After finishing, update this file with completion status.
When finished, update this section:
- Started: (date)
- Completed: (date)
- Commits created: X/13
- Samples migrated: X/12
- Documentation updated: Yes/No
- Tested all samples: Yes/No
- Pushed to remote: Yes/No (branch name: ________)
- Merged to main: Yes/No
Notes: (Add any notes about issues encountered, deviations from plan, etc.)
End of Migration Plan