Hangfire in .NET Core 3.1 console app with Docker

Tags: #<Tag:0x00007fb50dc27378>

Hello,

I’m already deployed a .NET Core 3.1 console app only used by Hangfire in Windows Services with success.
Now, I’m trying with Docker but it is not working as I expected.

Below the code which is okay in a simple console app run with Win10.

static internal class Program
    {
        #region Constants

        private const string AppSettingsName = "appsettings";

        #endregion Constants

        #region Static methods

        public static async Task Main(string[] args)
        {
            IConfigurationRoot config = new ConfigurationBuilder().AddCommandLine(args).Build();

            try
            {
                Console.WriteLine("Test1");

                IHostBuilder hostBuilder = new HostBuilder()
                    .ConfigureHostConfiguration(configHost =>
                    {
                        configHost.SetBasePath(Directory.GetCurrentDirectory())
                        .AddEnvironmentVariables()
                        .AddCommandLine(args)
                        .AddConfiguration(config);
                    })
                    .ConfigureAppConfiguration((hostContext, configApp) =>
                    {
                        configApp.SetBasePath(Directory.GetCurrentDirectory())
                                .AddJsonFile($"{AppSettingsName}.json", optional: false)
                                .AddJsonFile($"{AppSettingsName}.{hostContext.HostingEnvironment.EnvironmentName}.json", optional: false);
                    })
                    .ConfigureServices((hostContext, services) =>
                    {
                        services.AddDbContext<IStorage, ClientAreaContext>(options => options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
                                                                                            .UseNpgsql(hostContext.Configuration.GetConnectionString("DefaultConnection")), ServiceLifetime.Transient);
                        services
                            .AddLocalization(hostContext.Configuration)
                            .AddJobSettings(hostContext.Configuration)
                            .AddJobs()
                            .AddHostedService<HangfireLaunchService>();
                    })
                    .ConfigureLogging((hostContext, logging) =>
                    {
                        logging.ClearProviders();

                        logging.AddConfiguration(hostContext.Configuration.GetSection("Logging"));

                Console.WriteLine("Test2");

                await hostBuilder.Build().RunAsync();
            }
            catch (Exception e)
            {
                using (var loggerFactory = new LoggerFactory())
                {
                    ILogger logger = loggerFactory.CreateLogger("HangfireStart");
                    logger.LogError(e, "OH");
                }
            }
        }

    internal class HangfireLaunchService : IHostedService
    {
        #region Instance variables

        private readonly IServiceProvider _serviceProvider; 
        private readonly HangfireSettings _settings;     
        private readonly ILogger _logger;               

        private BackgroundJobServer _hangfireServer; 

        #endregion Instance variables

        #region Constructors

        public HangfireLaunchService(
            IOptions<HangfireSettings> hangfireSettings,
            IServiceProvider serviceProvider,
            ILogger<HangfireLaunchService> logger)
        {
            _settings = hangfireSettings.Value;
            _serviceProvider = serviceProvider;
            _logger = logger;
        }

        #endregion Constructors

        #region Methods

        /// <summary>
        /// <see cref="IHostedService.StartAsync(CancellationToken)"/>
        /// </summary>
        public Task StartAsync(CancellationToken cancellationToken)
        {
            BackgroundJobServerOptions backgroundJobServerOptions = new BackgroundJobServerOptions
            {
                Activator = new HangfireActivator(_serviceProvider),
                ServerName = "Batchs",
                WorkerCount = 2
            };

            // Create an instance of Hangfire Server and start it.
            _hangfireServer = new BackgroundJobServer(backgroundJobServerOptions, new PostgreSqlStorage(_settings.ConnectionString));
            GlobalConfiguration.Configuration.UsePostgreSqlStorage(_settings.ConnectionString);

            _logger.LogInformation("Hangfire started...");

            Console.WriteLine("Hangfire Started...");

            ConfigureRecurringTasks();

            return Task.CompletedTask;
        }

        /// <summary>
        /// <see cref="IHostedService.StopAsync(CancellationToken)"/>
        /// </summary>
        public Task StopAsync(CancellationToken cancellationToken)
        {
            _hangfireServer.Dispose();
            _logger.LogWarning("Hangfire Server stoped.");

            return Task.CompletedTask;
        }

        /// <summary>
        /// Is used to configure recurring tasks in Hangfire.
        /// </summary>
        private void ConfigureRecurringTasks()
        {
            IEnumerable<IRecurringJob> recurringTasks = _serviceProvider.GetServices<IRecurringJob>();
            IEnumerable<string> jobIds = recurringTasks.Select(x => x.Name).ToList();

            using (IStorageConnection connection = JobStorage.Current.GetConnection())
            {
                foreach (RecurringJobDto recurringJob in connection.GetRecurringJobs().Where(recurringJob => !jobIds.Contains(recurringJob.Id)))
                {
                    RecurringJob.RemoveIfExists(recurringJob.Id);
                }
            }

            foreach (IRecurringJob task in recurringTasks)
            {
                if (task is ISynchronousJob synchronousTask)
                {
                    RecurringJob.AddOrUpdate(
                        synchronousTask.Name,
                        () => synchronousTask.Execute(CancellationToken.None),
                        synchronousTask.CronExpression);
                }
                else if (task is IAsynchronousJob asynchronousTask)
                {
                    RecurringJob.AddOrUpdate(
                        asynchronousTask.Name,
                        () => asynchronousTask.ExecuteAsync(CancellationToken.None),
                        asynchronousTask.CronExpression);
                }
            }
        }

        #endregion Methods
    }

When I run docker run -i MyImage, I can read “Test1” and “Test2”, that’s it.

Why the logger data are not displayed ?
Why the app exit with code 0 ?

Some usefull source :