.NET Aspire  

.NET Aspire 13.5 File Inputs: Testing Interactive Setup Workflows

Introduction

Setting up a distributed application can involve a surprising number of small manual steps.

A developer may need to import a configuration file, load test data, provide a certificate, select a local settings file, or upload a fixture before a service can start working correctly.

Usually, these tasks are documented as instructions:

1. Find the file.
2. Copy it to a specific folder.
3. Rename it.
4. Start the application.
5. Run the import command.

The process works, but it puts the responsibility on the developer to know exactly what to do.

.NET Aspire 13.5 introduces file-input support for interactive resource commands. This allows an Aspire application to request a file directly through the dashboard, apply restrictions such as file type and size, and pass the selected file to the AppHost for processing. Microsoft specifically highlights file import as one of the new interaction capabilities in Aspire 13.5.

This is a small feature, but it can make a noticeable difference to developer experience because setup operations can become part of the application rather than a separate collection of manual instructions.

What Are Aspire File Inputs?

File inputs allow a custom Aspire resource command to ask the developer to select a file.

The basic workflow is:

Aspire Dashboard
       |
       v
Resource Command
       |
       v
File Picker
       |
       v
Developer Selects File
       |
       v
AppHost Receives File
       |
       v
Validate
       |
       v
Process
       |
       v
Show Result

For example, a developer could select:

config.json

from the dashboard and have the AppHost import it.

The feature is especially useful for development-time workflows where manually copying files is inconvenient.

Why File Inputs Are Useful

Consider a distributed application with:

Orders API
Inventory API
Payments API
PostgreSQL
Redis

A developer wants to load sample inventory data.

Without an interactive file input:

Download sample-data.json
        |
        v
Find local project directory
        |
        v
Copy file
        |
        v
Run import command
        |
        v
Check logs

With a dashboard command:

Inventory API
    |
    +-- Import Sample Data
             |
             v
        Select File
             |
             v
         Validate
             |
             v
          Import

The second workflow is easier to discover, especially for developers who are new to the repository.

File Inputs Are a Development Experience Feature

It is important to understand where this capability fits.

The file-input API is primarily useful for development-time and AppHost workflows.

It does not mean an ASP.NET Core application should expose arbitrary filesystem access through a web interface.

The intended model is closer to:

Developer
    |
    v
Aspire Dashboard
    |
    v
AppHost Interaction
    |
    v
Development Resource

rather than:

Internet User
    |
    v
Upload API
    |
    v
Production File System

That distinction matters for security and architecture.

A Typical File Import Scenario

Imagine an application that needs a local JSON configuration file.

The desired user experience could be:

+--------------------------------------+
| Import Configuration                 |
|                                      |
| Select configuration file            |
|                                      |
| [ Choose File ]                      |
|                                      |
| Accepted: .json                      |
| Maximum size: 5 MB                   |
|                                      |
|              [ Import ]              |
+--------------------------------------+

After the user selects the file:

File
 |
 v
Read Stream
 |
 v
Validate Size
 |
 v
Validate Format
 |
 v
Parse JSON
 |
 v
Validate Schema
 |
 v
Apply Configuration

The important part is that file selection is only the first step.

The application still needs to validate the content.

File Type Restrictions

A useful file-input workflow should restrict the files that developers can select.

For example:

Accepted files:
.json
.yaml
.yml

If the workflow only expects JSON, there is little reason to allow every file type.

Conceptually:

Allowed:
config.json

Rejected:
image.png
archive.zip
unknown.exe

This improves the user experience and reduces accidental input.

However, file extension filtering is not a security boundary.

A malicious or incorrect file can still have a misleading extension.

The application should inspect and validate the actual content.

File Size Limits

File size should also be controlled.

Suppose the workflow only needs a small configuration file.

A sensible limit might be:

Maximum:
5 MB

The exact limit depends on the workflow.

The reason is simple: an interactive file upload should not allow an accidentally huge file to consume unnecessary memory, disk space, or processing time.

A robust processing pipeline is:

Selected File
      |
      v
Size Check
      |
      +---- Too Large ----> Reject
      |
      v
Format Check
      |
      +---- Invalid ------> Reject
      |
      v
Content Validation
      |
      +---- Invalid ------> Reject
      |
      v
Process

Microsoft's Aspire 13.5 release notes specifically describe file-input capabilities that include file-type restrictions and maximum file-size limits.

Working With the Uploaded File

The AppHost receives the selected file as a stream.

A simplified processing method could look like:

public async Task ImportConfigurationAsync(Stream file)
{
    using var reader = new StreamReader(file);

    var json = await reader.ReadToEndAsync();

    if (string.IsNullOrWhiteSpace(json))
    {
        throw new InvalidOperationException(
            "The configuration file is empty.");
    }

    // Parse and validate the configuration.
}

The important point is to process the stream rather than assuming that the file already exists somewhere on the developer's machine.

The AppHost becomes responsible for handling the file received from the interaction.

Deserialize Only After Validation

Suppose the expected configuration looks like:

{
  "environment": "Development",
  "apiBaseUrl": "https://localhost:5001",
  "enableSeedData": true
}

A corresponding model might be:

public sealed class AppConfiguration
{
    public string Environment { get; set; } = string.Empty;

    public string ApiBaseUrl { get; set; } = string.Empty;

    public bool EnableSeedData { get; set; }
}

The file can then be deserialized:

var configuration =
    JsonSerializer.Deserialize<AppConfiguration>(
        json);

if (configuration is null)
{
    throw new InvalidOperationException(
        "Invalid configuration.");
}

But deserialization alone is not validation.

You should still verify required values:

if (string.IsNullOrWhiteSpace(configuration.Environment))
{
    throw new InvalidOperationException(
        "Environment is required.");
}

if (string.IsNullOrWhiteSpace(configuration.ApiBaseUrl))
{
    throw new InvalidOperationException(
        "API base URL is required.");
}

This prevents an apparently valid JSON document from becoming invalid application state.

File Inputs and Configuration Import

One practical use case is importing local environment configuration.

For example:

Development
    |
    +-- appsettings.json
    +-- appsettings.Development.json
    +-- local-secrets.json

Instead of asking every developer to manually copy these files, Aspire can provide an explicit import workflow.

The command could be:

Orders API
    |
    +-- Import Local Configuration

The developer selects the file.

The AppHost validates it.

The application then receives the required development configuration.

This can make onboarding considerably easier.

File Inputs and Test Data

Another useful scenario is loading test data.

Imagine an e-commerce application that needs a large product dataset for local development.

The file could contain:

{
  "products": [
    {
      "name": "Laptop",
      "price": 1200
    },
    {
      "name": "Keyboard",
      "price": 80
    }
  ]
}

The workflow becomes:

Select products.json
        |
        v
Validate JSON
        |
        v
Validate Product Schema
        |
        v
Insert Test Data
        |
        v
Show Import Result

This is much easier than asking developers to learn a database import command before they can start testing the application.

Showing Import Progress

File imports can take time.

For example:

products.json
       |
       v
10,000 records
       |
       v
Validate
       |
       v
Transform
       |
       v
Insert

A progress workflow can make the operation easier to understand.

Conceptually:

Importing products...

[############--------] 63%

6,300 / 10,000 records

Aspire 13.5 also adds progress-dialog support to the Interaction Service. Microsoft identifies the progress-dialog functionality as experimental in this release, so teams should evaluate it carefully before depending on it as a stable API.

File Inputs vs Command-Line Arguments

Developers sometimes need to decide whether a workflow should use a file picker or a command-line argument.

Both can be useful.

RequirementFile InputCommand Argument
Developer selects a local fileExcellentModerate
Dashboard-friendlyExcellentGood
Script-friendlyLimitedExcellent
CLI automationLimitedExcellent
File-specific UIExcellentNo
Easy for beginnersExcellentModerate
Repeatable automationModerateExcellent

A good approach is to support both where the workflow needs both interactive and automated usage.

For example:

Dashboard:
Import Data → Choose File

CLI:
Import Data --file products.json

Aspire's interaction documentation recommends command arguments for scenarios where command input needs to work across both dashboard and CLI contexts.

File Inputs vs Web Upload APIs

These two concepts should not be confused.

A production upload API might look like:

Browser
   |
   v
POST /api/files
   |
   v
ASP.NET Core
   |
   v
Object Storage

An Aspire development-time file input looks more like:

Developer
   |
   v
Aspire Dashboard
   |
   v
AppHost
   |
   v
Development Resource

The first is part of an application's user-facing architecture.

The second is part of the developer experience.

This distinction helps prevent accidentally putting development-only behavior into production APIs.

Testing File Inputs

A useful test plan should cover more than successful imports.

Test 1: Valid File

config.json
    |
    v
Accepted
    |
    v
Imported

Test 2: Wrong Extension

config.exe
    |
    v
Rejected

Test 3: Oversized File

50 MB
    |
    v
Maximum = 5 MB
    |
    v
Rejected

Test 4: Invalid JSON

config.json
    |
    v
Malformed JSON
    |
    v
Rejected

Test 5: Valid JSON but Invalid Schema

{
  "environment": 123
}

The file is valid JSON but does not satisfy the application's expected structure.

It should be rejected.

Test 6: Empty File

0 bytes
   |
   v
Rejected

Test 7: Large Valid Dataset

Test a realistic file size and record count.

This is important because a workflow that works with 20 records may behave very differently with 100,000 records.

A Practical Validation Pipeline

A production-quality development import can follow this structure:

public async Task ImportAsync(Stream file)
{
    // 1. Validate the input stream.
    if (file is null)
    {
        throw new ArgumentNullException(nameof(file));
    }

    // 2. Read the content.
    using var reader = new StreamReader(file);

    var json = await reader.ReadToEndAsync();

    // 3. Validate content.
    if (string.IsNullOrWhiteSpace(json))
    {
        throw new InvalidOperationException(
            "The selected file is empty.");
    }

    // 4. Deserialize.
    var model = JsonSerializer.Deserialize<AppConfiguration>(
        json);

    if (model is null)
    {
        throw new InvalidOperationException(
            "The file contains invalid configuration.");
    }

    // 5. Validate business rules.
    Validate(model);

    // 6. Apply the configuration.
    await ApplyConfigurationAsync(model);
}

The important design principle is that the interaction layer should not become responsible for business rules.

It should collect the file.

The application should validate and process it.

Keeping File Processing Out of the AppHost

An AppHost can become difficult to maintain if all import logic is placed directly inside it.

A better design is:

AppHost
   |
   +--> Interaction
   |
   +--> File Selection
   |
   v
Configuration Service
   |
   +--> Validation
   +--> Parsing
   +--> Transformation
   +--> Import

For example:

public interface IConfigurationImporter
{
    Task ImportAsync(
        Stream file,
        CancellationToken cancellationToken);
}

Then the AppHost simply coordinates the interaction.

This keeps the actual processing logic reusable and testable.

Handling Errors Gracefully

A failed file import should provide a useful message.

Bad:

Operation failed.

Better:

Import failed.

The selected configuration is missing
the required "apiBaseUrl" property.

Even better, if appropriate:

Import failed.

Missing property:
apiBaseUrl

Expected:
string URL

Good error messages reduce debugging time.

Common Mistakes

Mistake 1: Treating File Extension as Validation

A file called:

config.json

is not automatically valid JSON.

Always validate the content.

Mistake 2: No File Size Limit

Large files can consume unnecessary resources.

Set an appropriate maximum size for the workflow.

Mistake 3: Reading Everything Into Memory

For small configuration files this may be acceptable.

For large datasets, streaming processing may be more appropriate.

Mistake 4: Putting Business Logic in the AppHost

Keep parsing and domain-specific processing in dedicated services.

Mistake 5: Assuming Dashboard Input Works Everywhere

Dashboard interactions and CLI workflows have different capabilities.

Use command arguments when automation is important.

Mistake 6: Treating Experimental APIs as Stable

Some Aspire 13.5 interaction features are experimental. Verify the current API and diagnostics before building a long-lived internal framework around them.

Troubleshooting

ProblemWhat to Check
File picker does not appearVerify the resource command and file-input configuration
File rejected unexpectedlyCheck accepted extensions and size limits
Import failsValidate file content and schema
JSON parsing failsCheck encoding and document structure
Large file causes slow processingConsider streaming and batching
CLI workflow does not workUse command arguments where appropriate
Progress UI does not behave as expectedCheck experimental API requirements
AppHost becomes difficult to maintainMove processing into a dedicated service
File works locally but not in another environmentCheck environment-specific paths and dependencies

Advantages

Better Developer Onboarding

New developers can discover setup operations from the Aspire dashboard instead of reading a long setup document.

Fewer Manual File-Copy Steps

Developers do not need to remember where configuration or test files belong.

Clearer Development Workflows

The dashboard can expose operations such as:

Import Configuration
Import Test Data
Load Fixture
Reset Environment

Better Validation

The workflow can validate file type, size, schema, and content before applying changes.

Good Fit for Distributed Applications

File-based setup can be associated directly with the resource that needs it.

Disadvantages and Limitations

Development-Focused

This capability is primarily useful for developer workflows and should not automatically be treated as a replacement for production upload APIs.

Experimental Features

Some related interaction capabilities in Aspire 13.5 are experimental.

Additional AppHost Complexity

Interactive commands add code and configuration to the AppHost.

File Processing Still Requires Engineering

Selecting a file does not solve validation, parsing, schema management, or data-import problems.

Dashboard and CLI Differences

A workflow designed only around dashboard interactions may not translate directly to automated CLI scenarios.

Best Practices

Keep File Inputs Purpose-Specific

Do not create a generic "Upload Anything" command.

Prefer:

Import Configuration

or:

Load Product Test Data

The purpose should be clear.

Restrict File Types

Only accept formats the operation understands.

Set a Reasonable Size Limit

Do not allow unnecessarily large files.

Validate Content

Check structure, required fields, data types, and business rules.

Keep Processing Logic Separate

Use dedicated services rather than placing all import logic inside the AppHost.

Provide Useful Errors

Tell developers what went wrong and how to fix it.

Support Automation When Needed

If the workflow needs to run in CI or through the CLI, consider command arguments instead of relying exclusively on interactive file selection.

Test Realistic Files

Use both small and representative production-like development datasets.

A Practical Aspire Setup

A simplified architecture could look like:

DistributedApplication
        |
        v
   Orders API
        |
        +-- Import Configuration
        |
        +-- Load Test Data
        |
        v
Interaction Service
        |
        v
     File Input
        |
        v
ConfigurationImporter
        |
        +-- Validate
        +-- Parse
        +-- Transform
        +-- Apply

The AppHost provides the interaction.

The importer owns the actual application logic.

That separation keeps the design easier to maintain.

Conclusion

.NET Aspire 13.5's file-input capability is a small feature with a practical benefit: it can turn manual development setup steps into simple, discoverable workflows inside the Aspire dashboard. Instead of telling every developer where to copy a configuration file or how to run a data-import command, the application can provide an "Import" action and guide them through the process. The important part is not to stop at the file picker. Files still need size limits, format checks, schema validation, and proper error handling. It is also worth remembering that dashboard interactions and CLI workflows are not identical, so command arguments may be a better option when automation is required. Used thoughtfully, file inputs can make distributed .NET projects easier to set up and much more comfortable to work with during day-to-day development.