Documentation

reference/aspire-hot-reload-and-dev-workflow.md

Aspire Hot Reload and Development Workflow

Last Updated: January 2025
Status: Current assessment of .NET Aspire capabilities
Related: Aspire overview

Overview

This document captures our analysis of hot reload and automatic restart capabilities in .NET Aspire for Dynaplex development. It documents what's currently possible, what isn't, and the trade-offs of various approaches.

The Developer Experience Vision

The ideal development workflow would:

  1. Start the AppHost once with all infrastructure running
  2. Make code changes to individual components
  3. Have only the changed component automatically rebuild and restart
  4. Infrastructure and other services remain running
  5. No full AppHost restart required

Current State of .NET Aspire (January 2025)

What Works Today

1. Hot Reload for Code Changes (Within Services)

.NET Hot Reload works within individual running services for many types of code changes:

  • Method body changes
  • Adding methods/properties to existing types
  • Lambda expression changes
  • Most Blazor UI changes

Limitation: When Hot Reload encounters "rude edits" (structural changes like adding interfaces, changing method signatures, etc.), it requires a full AppHost restart, not just the affected service.

2. Persistent Container Lifetimes (Infrastructure Persistence)

Aspire supports persistent container lifetimes for infrastructure resources:

var postgres = builder.AddPostgres("postgres")
    .WithLifetime(ContainerLifetime.Persistent);

var redis = builder.AddRedis("cache")
    .WithLifetime(ContainerLifetime.Persistent);

Benefit: Infrastructure containers (databases, caches, message queues) persist across AppHost restarts, dramatically reducing restart times.

Status in Dynaplex: ✅ Implemented across all infrastructure resources (see InfrastructureBuilder.cs and DynaplexInfrastructureExtensions.cs).

3. Run Without Debugging (Ctrl+F5 Workflow)

When running the AppHost with Ctrl+F5 (without debugger):

  • Individual projects can be rebuilt independently
  • Those projects restart automatically
  • Infrastructure remains running
  • Not fully automatic, but workable

4. dotnet watch on Individual Services

Can run dotnet watch directly on service projects for automatic rebuild/restart of that specific service while keeping other services running.

What Doesn't Work Today

1. Automatic Restart of Individual Services

When Hot Reload fails (rude edit), Aspire requires restarting the entire AppHost, not just the affected service.

GitHub Issues Tracking This:

Status: Feature requests active as of January 2025, not yet implemented.

2. Manual Restart from Dashboard

Cannot manually stop/restart individual services from the Aspire Dashboard.

Status: Feature request #295.

3. Better dotnet watch Integration with AppHost

dotnet watch on the AppHost doesn't properly cascade to orchestrated services.

Status: Known issue #620.

Evaluated Alternatives

Container-Based Development

Concept: Package Dynaplex components as containers and use Aspire's container orchestration.

Potential Benefits

  • More control over individual service lifecycles
  • Could use persistent lifetimes for stable components
  • Closer to production deployment model
  • Volume mounts + watch modes for auto-rebuild

Significant Drawbacks

  • Slower build loop (container image building is slower than .NET builds)
  • More complex debugging (debugging inside containers is harder)
  • Less integrated with .NET tooling (lose seamless IDE integration)
  • Requires significant changes to development workflow
  • Moves away from Aspire's strengths for .NET development

Decision: Not pursuing at this time. The drawbacks outweigh the benefits for our current workflow.

Current Dynaplex Approach

What We've Implemented

1. Persistent Infrastructure Lifetimes

All infrastructure resources use ContainerLifetime.Persistent:

  • PostgreSQL database
  • Azure App Configuration emulator
  • Azurite (blob storage emulator)
  • Redis (for Superset analytics)
  • Superset analytics container

Impact: Infrastructure no longer restarts between AppHost sessions. This is the single biggest improvement to restart times.

Location:

  • strata/orchestration/src/Acsis.Dynaplex.Strata.Orchestration/InfrastructureBuilder.cs
  • strata/orchestration/src/Acsis.Dynaplex.Strata.Orchestration/DynaplexInfrastructureExtensions.cs

2. Project-Based Service Orchestration

Component services remain as AddProject resources (not containers):

  • Maintains excellent debugging experience
  • Seamless IDE integration
  • Fast compilation
  • Full .NET tooling support

For Minor Changes (Hot Reload Compatible)

  1. Start AppHost normally (F5)
  2. Make code changes
  3. Hot Reload applies automatically
  4. Test changes immediately

Best for: Method implementations, UI tweaks, lambda expressions

For Structural Changes (Rude Edits)

  1. Run AppHost with Ctrl+F5 (no debugger)
  2. Make structural changes
  3. Rebuild affected project
  4. Project restarts automatically
  5. Infrastructure and other services remain running

Best for: Adding interfaces, changing signatures, adding dependencies

For Heavy Refactoring

  1. Accept that full restart is needed
  2. Infrastructure will persist (fast restart)
  3. Only component services need to reinitialize

Best for: Multi-component changes, dependency updates

Performance Characteristics

Without Persistent Containers

  • Full restart: 30-60 seconds (cold start)
  • Infrastructure initialization: 20-40 seconds
  • Service startup: 10-20 seconds

With Persistent Containers (Current)

  • Full restart: 10-20 seconds (warm start)
  • Infrastructure initialization: 0 seconds (already running)
  • Service startup: 10-20 seconds

Improvement: ~50-70% reduction in restart time

Future Outlook

The Aspire team is aware of the need for individual service restart capabilities. Based on community engagement on GitHub issues, this is likely to be implemented in future versions.

Expected Future Capabilities

  • Dashboard controls to stop/start individual services
  • Automatic detection of which services need restart
  • More granular watch and reload capabilities
  • Better integration with dotnet watch

What This Means for Dynaplex

When these features arrive, we'll be able to:

  1. Keep infrastructure running (we already do this)
  2. Selectively restart only changed services (new capability)
  3. Have a truly seamless hot reload experience

Our current architecture (project-based services + persistent infrastructure) positions us well to take advantage of these features when they become available.

Guidance for Developers

Structure Your Work Sessions

  • Hot-reloadable work: Group method implementation, UI tweaks, and logic changes
  • Structural work: Batch interface additions, signature changes, and dependency updates
  • Heavy refactoring: Accept that restart is needed, rely on fast infrastructure persistence

Optimize for Hot Reload Success

  • Avoid structural changes during active debugging sessions
  • Use partial classes to separate hot-changeable code from structural code
  • Commit structural changes when you're willing to do a restart

Take Advantage of Persistent Infrastructure

  • Don't manually stop containers between sessions
  • Infrastructure accumulates state (databases, caches) naturally
  • Clean up only when needed: docker container prune or podman container prune

Cleanup Commands

List Persistent Containers

docker ps -a --filter "name=postgres"
docker ps -a --filter "name=redis"
docker ps -a --filter "name=azurite"
docker ps -a --filter "name=config"
docker ps -a --filter "name=superset"

Remove Persistent Containers (when needed)

docker stop postgres redis azurite config superset
docker rm postgres redis azurite config superset

Or with Podman:

podman stop postgres redis azurite config superset
podman rm postgres redis azurite config superset

References

GitHub Issues


Conclusion: Our current approach (persistent infrastructure + project-based services) provides excellent developer experience today while positioning us to take advantage of future Aspire enhancements. The container-based alternative has been evaluated and rejected due to significant drawbacks that outweigh its benefits for our .NET-focused development workflow.