When using .NET Aspire Hosted App, most samples use Environment Variables when configuring your services. However, when you are migrating existing services, the chance is that you are using the Options pattern for .NET Core.

So, I decided to write this post in order to share how I achieved this.

Using Environment variables

The default approach seems to be the following.

The AppHost

In the AppHost, you are just adding the service (or project, as they call it in Aspire) and you can pass environment variables to the services.

var builder = DistributedApplication.CreateBuilder(args);

builder
    .AddProject<MySolution_Service1>("worker")
    .WithEnvironment("config1", "value1")
    .WithEnvironment("config2", "value2");

await builder.Build().RunAsync();

The Service

In the Service project, you can easily access the environment variables, by using this code.

Console.WriteLine(Environment.GetEnvironmentVariable("config1"));

Or you can use the Configuration builder for it.

builder.Configuration.GetValue<string>("config1");

Using one centralized approach for Options pattern

In order to have the default AppSettings shared from the Hosting project, and made available with the Micro services projects, we define an appsettings.base.json file in the Hosting project and have that automatically copied to all the services that need it. Every service can then still define an appsettings.json file to override or add extra configuration items per service.

The actual file reference

  1. Define a file appsettings.base.json in the Hosting project.
  2. Update the services project(s) files to indicate files should be copied from the hosting project to the service project itself.
Name="CopySharedAppSettings" AfterTargets="Build">
    
    
         Include="..\MyApp.Host\appsettings.base.json" />
    

    
     SourceFiles="@(SharedFiles)" DestinationFolder="$(ProjectDir)" />


     Update="appsettings.base.json">
        PreserveNewest

Injecting the IOptions

In .NET Aspire, runtime configuration is typically done, through the Service Defaults. In this project, we will be adding the configuration method to inject all the relevant (common) Options into the services. Obviously, this can also be done by performing this in the services project itself.

As you can see, we are first loading the base file, and after that, the overrides from the service specific file. And, as this mainly focuses on the dev/local experience (with the appsettings file), we also inject the Environment variables, so that we can also leverage the deployment configuration when deployed onto a cluster.

private static TBuilder ConfigureOptions<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
{
    var cfgBuilder = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory());

    cfgBuilder
        .AddJsonFile($"appsettings.base.json", true, true)
        .AddJsonFile($"appsettings.json", true, true)
        .AddEnvironmentVariables();

    var configuration = cfgBuilder.Build();

    builder.Services.Configure<SqlOptions>(configuration.GetSection("sql"));
    return builder;
}

And we are calling this method from the "main method" of the Service Defaults project:

public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
    {
        builder.ConfigureOpenTelemetry();

        builder.ConfigureOptions();
        // ... skipped for brevity
        return builder;
    }