Search code examples
c#.netasp.net-core.net-core

.NET 8 windows service thinks hosting environment is production when debugging locally


We have a .NET 8 application that we are running as a Windows service.

Problem: when doing IHostBuilder.ConfigureAppConfiguration and checking the IHost.HostingEnvironment, it shows up as Production.

It looks like IHostBuilder sees the environment variable as Production by default?

Nowhere in the project is it set to run as Production.

I checked the following places for ASPNETCORE_ENVIRONMENT value:

  • launchsettings.json - it's set to Development
  • system / user environment variables - ASPNETCORE_ENVIRONMENT is not set there, so it should pick Development by default
  • whole project - nowhere is ASPNETCORE_ENVIRONMENT being set to Production.

Does anyone know why this happens and how to fix this?

Program.cs code:

using LinqToDB.EntityFrameworkCore;
using PosJobs.LT.Server;
using PosJobs.LT.Server.Ioc;
using Sentry.Serilog;
using Serilog;
using Serilog.Events;
using Serilog.Formatting.Compact;

const long LogFileSizeLimit = 5 * 1024 * 1024;
var baseDirectory = AppContext.BaseDirectory;

IHost host = Host.CreateDefaultBuilder(args)
    .UseWindowsService(options =>
    {
        options.ServiceName = "PosJobs.LT";
    })
    .ConfigureAppConfiguration((ctx, builder) =>
    {
        var environment = ctx.HostingEnvironment.EnvironmentName;

        builder.SetBasePath(baseDirectory);
        builder.AddJsonFile("appsettings.json", false, true);
        builder.AddJsonFile($"appsettings.{environment}.json", true, true);
        builder.AddEnvironmentVariables();
    })
    .ConfigureServices((ctx, services) =>
    {
        var configuration = ctx.Configuration;

        services.ConfigureHangfire(ctx);
        services.ConfigureOptions(configuration);
        services.ConfigureServices(configuration);
        services.ConfigureDbContexts(configuration);
    })
    .UseSerilog((hostingContext, loggerConfiguration) =>
    {
        var f = new FileInfo(baseDirectory);
        var drive = Path.GetPathRoot(f.FullName);

        var logPath = @$"{drive}\Logs\PosJobs.LT\log_.json";

        loggerConfiguration
            .MinimumLevel.Information()
            .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Error)
            .MinimumLevel.Override("Microsoft.AspNetCore.Hosting.Diagnostics", LogEventLevel.Error)
            .MinimumLevel.Override("Microsoft.AspNetCore.Routing.EndpointMiddleware", LogEventLevel.Error)
            .MinimumLevel.Override("Microsoft.AspNetCore.Authorization.DefaultAuthorizationService", LogEventLevel.Error)
            .MinimumLevel.Override("Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker",
                LogEventLevel.Error)
            .MinimumLevel.Override("Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware", LogEventLevel.Error)
            .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Error)
            .MinimumLevel.Override("Microsoft.AspNetCore.Mvc", LogEventLevel.Error)
            .WriteTo.Console()
            .WriteTo.File(new CompactJsonFormatter(),
                logPath,
                shared: true,
                restrictedToMinimumLevel: LogEventLevel.Information,
                retainedFileCountLimit: 90,
                rollingInterval: RollingInterval.Day,
                rollOnFileSizeLimit: true,
                fileSizeLimitBytes: LogFileSizeLimit);

        if (!hostingContext.HostingEnvironment.IsDevelopment())
        {
            loggerConfiguration.WriteTo.Sentry(options =>
            {
                var optionsToSet = hostingContext.Configuration.GetSection("SentrySerilog").Get<SentrySerilogOptions>();

                options.Dsn = optionsToSet.Dsn;
                options.Environment = optionsToSet.Environment;
                options.StackTraceMode = optionsToSet.StackTraceMode;
                options.MinimumEventLevel = optionsToSet.MinimumEventLevel;
                options.MinimumBreadcrumbLevel = optionsToSet.MinimumBreadcrumbLevel;
                options.InitializeSdk = true;
            });
        }
    })
    .Build();

IConfiguration config = host.Services.GetRequiredService<IConfiguration>();
config.RegisterJobs(host.Services);

LinqToDBForEFTools.Initialize();

await host.RunAsync();

Update #1

After doing some reading, apparently the generic IHost builder check for DOTNET_ variable, not ASPNETCORE_. Added this variable to launchsettings.json and now it works correctly.

But my question still remains: why does generic IHost builder see DOTNET_ENVIRONMENT as Production by default?


Solution

  • That is because the default value is Production if neither variable is set.

    Production: The default if DOTNET_ENVIRONMENT and ASPNETCORE_ENVIRONMENT have not been set.

    https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-8.0