HangfireIO / Hangfire

An easy way to perform background job processing in .NET and .NET Core applications. No Windows Service or separate process required
https://www.hangfire.io
Other
9.41k stars 1.7k forks source link

Jobstorage.current property not initalized? #1710

Open LymeHS opened 4 years ago

LymeHS commented 4 years ago

Hello, we're new to hangfire and trying to get it running. We're using an old ASP.net build with a global.asax file instead of a startup file. We have two different enviorments, dev and staging.

The code is identical, except for the connection string for the database, as it is located on a different server. Everything works great in Dev. The tables get created, the jobs run. But when we migrated over to staging, we're getting the following error, and the Hangfire tables fail to create:

JobStorage.Current property value has not been initialized. You must set it before using Hangfire Client or Server API

Like I said, everything works fine in our dev enviorment, but in staging we get that error and the tables also fail to create. I'm assuming it's an issue connection to the database or something with permissions? Can anyone that understands this error elaborate on that at all?

    private IEnumerable<IDisposable> GetHangfireServers()
    {
        _kernel = new StandardKernel(new PartnerIntegrationServiceNinjectModule());
        GlobalConfiguration.Configuration
                           .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
                           .UseSimpleAssemblyNameTypeSerializer()
                           .UseRecommendedSerializerSettings()
                           .UseSqlServerStorage(_kernel.Get<ISettings>().HangfireConnectionString, new SqlServerStorageOptions
                           {
                               CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
                               SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
                               QueuePollInterval = TimeSpan.Zero,
                               UseRecommendedIsolationLevel = true,
                               DisableGlobalLocks = true,
                               SchemaName = "Hangfire"
                           }) ;

        yield return new BackgroundJobServer();
    }

        base.OnApplicationStarted();
        HangfireAspNet.Use(GetHangfireServers);
philippdrebes commented 4 years ago

We experience the same effect. With ASPNETCORE_ENVIRONMENT=Development everything works fine, but when we switch to ASPNETCORE_ENVIRONMENT=Production, we get the same error. We haven't found a solution yet.

Other posts on the internet suggest adding

JobStorage.Current = new PostgreSqlStorage(configuration.GetConnectionString("DefaultConnection"));

but this doesn't help either.

MarcolinoPT commented 3 years ago

Getting the same error as above but in my case, I'm trying to schedule a job but the server hasn't got the connection to PostgreSQL yet. A workaround was to add the jobs using the thread pool,

        // An extension method called during Startup.cs
        {
            ThreadPool.QueueUserWorkItem(DelayedJobs);
            return serviceCollection;
        }

        private static void DelayedJobs(object stateInfo)
        {
            // Delay startup for 5 minutes to be sure
            // everything is up and running
            Thread.Sleep(TimeSpan.FromMinutes(5));
            // Schedule jobs
            BackgroundJob.EnqueueT>(
                methodCall: doStuff => updateCampaignAnalytics.Run());
        }

Is there a way to save the job in memory before a connection is established?

54mu3l commented 2 years ago

Hi!

I have a similar behavior.

Since I upgraded my project from .NET Core 3.1 to .NET 6.0 with the minimal hosting model, I get the same error:

JobStorage.Current property value has not been initialized. You must set it before using Hangfire Client or Server API.

Even though the code (regarding Hangfire) hasn't changed (except of course it's now within the minimal hosting model). The code looks something like this:

...

// Add Hangfire services
builder.Services.AddHangfire(configuration => configuration
    .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
    .UseSimpleAssemblyNameTypeSerializer()
    .UseRecommendedSerializerSettings()
    .UsePostgreSqlStorage(builder.Configuration.GetConnectionString("HangfireConnection")));

...

builder.Services.AddHangfireServer();

...

var app = builder.Build();

...

if (builder.Environment.IsDevelopment())
{
    app.UseHangfireDashboard();
}

// remove all Recurring-Jobs
using (var connection = JobStorage.Current.GetConnection())
{
    foreach (var recurringJob in StorageConnectionExtensions.GetRecurringJobs(connection))
    {
        RecurringJob.RemoveIfExists(recurringJob.Id);
    }
}

// add those Recurring-Jobs we need
RecurringJob.AddOrUpdate<CronJobs>(cj => cj.MyMethod1(), Cron.Weekly);
RecurringJob.AddOrUpdate<CronJobs>(cj => cj.MyMethod2(), Cron.Hourly);

// log all added Recurring-Jobs
using (var connection = JobStorage.Current.GetConnection())
{
    foreach (var recurringJob in StorageConnectionExtensions.GetRecurringJobs(connection))
    {
        log.Info("add job=" + recurringJob.Id + " cron=" + recurringJob.Cron);
    }
}

...

app.Run();

This error only occurs in production environment. In my development environment I have no problems and everything works fine. This is probably since I'm using app.UseHangfireDashboard(); in development environment only. As soon as I comment app.UseHangfireDashboard(); out, the exception also occurs in my development environment.

54mu3l commented 2 years ago

I found a workaround. Adding the line app.Services.GetService<IBackgroundJobClient>(); seems to fix this:

...

// Add Hangfire services
builder.Services.AddHangfire(configuration => configuration
    .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
    .UseSimpleAssemblyNameTypeSerializer()
    .UseRecommendedSerializerSettings()
    .UsePostgreSqlStorage(builder.Configuration.GetConnectionString("HangfireConnection")));

...

builder.Services.AddHangfireServer();

...

var app = builder.Build();

...

if (builder.Environment.IsDevelopment())
{
    app.UseHangfireDashboard();
}
else
{
    app.Services.GetService<IBackgroundJobClient>(); // I added this line!
}

// remove all Recurring-Jobs
using (var connection = JobStorage.Current.GetConnection())
{
    foreach (var recurringJob in StorageConnectionExtensions.GetRecurringJobs(connection))
    {
        RecurringJob.RemoveIfExists(recurringJob.Id);
    }
}

// add those Recurring-Jobs we need
RecurringJob.AddOrUpdate<CronJobs>(cj => cj.MyMethod1(), Cron.Weekly);
RecurringJob.AddOrUpdate<CronJobs>(cj => cj.MyMethod2(), Cron.Hourly);

// log all added Recurring-Jobs
using (var connection = JobStorage.Current.GetConnection())
{
    foreach (var recurringJob in StorageConnectionExtensions.GetRecurringJobs(connection))
    {
        log.Info("add job=" + recurringJob.Id + " cron=" + recurringJob.Cron);
    }
}

...

app.Run();

But I believe this should be something Hangfire should handle internally...

Maybe Hangfire isn't ready for the new minimal hosting model?

odinserj commented 2 years ago

Please try using IRecurringJobManager service instead of static RecurringJob class and JobStorage service instead of static JobStorage.Current property (and the same with IBackgroundJobClient instead of BackgroundJob):

var backgroundJobs = app.Services.GetService<IBackgroundJobClient>();
var recurringJobs = app.Services.GetService<IRecurringJobManager>();
var storage = app.Services.GetService<JobStorage>();
using (var connection = storage.GetConnection())
{
    foreach (var recurringJob in StorageConnectionExtensions.GetRecurringJobs(connection))
    {
        recurringJobs.RemoveIfExists(recurringJob.Id);
    }
}

// add those Recurring-Jobs we need
recurringJobs.AddOrUpdate<CronJobs>(cj => cj.MyMethod1(), Cron.Weekly);
recurringJobs.AddOrUpdate<CronJobs>(cj => cj.MyMethod2(), Cron.Hourly);
wbehning commented 9 months ago

Hi!

I have a similar behavior.

Since I upgraded my project from .NET Core 3.1 to .NET 6.0 with the minimal hosting model, I get the same error:

JobStorage.Current property value has not been initialized. You must set it before using Hangfire Client or Server API.

Even though the code (regarding Hangfire) hasn't changed (except of course it's now within the minimal hosting model). The code looks something like this:

...

// Add Hangfire services
builder.Services.AddHangfire(configuration => configuration
    .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
    .UseSimpleAssemblyNameTypeSerializer()
    .UseRecommendedSerializerSettings()
    .UsePostgreSqlStorage(builder.Configuration.GetConnectionString("HangfireConnection")));

...

builder.Services.AddHangfireServer();

...

var app = builder.Build();

...

if (builder.Environment.IsDevelopment())
{
    app.UseHangfireDashboard();
}

// remove all Recurring-Jobs
using (var connection = JobStorage.Current.GetConnection())
{
    foreach (var recurringJob in StorageConnectionExtensions.GetRecurringJobs(connection))
    {
        RecurringJob.RemoveIfExists(recurringJob.Id);
    }
}

// add those Recurring-Jobs we need
RecurringJob.AddOrUpdate<CronJobs>(cj => cj.MyMethod1(), Cron.Weekly);
RecurringJob.AddOrUpdate<CronJobs>(cj => cj.MyMethod2(), Cron.Hourly);

// log all added Recurring-Jobs
using (var connection = JobStorage.Current.GetConnection())
{
    foreach (var recurringJob in StorageConnectionExtensions.GetRecurringJobs(connection))
    {
        log.Info("add job=" + recurringJob.Id + " cron=" + recurringJob.Cron);
    }
}

...

app.Run();

This error only occurs in production environment. In my development environment I have no problems and everything works fine. This is probably since I'm using app.UseHangfireDashboard(); in development environment only. As soon as I comment app.UseHangfireDashboard(); out, the exception also occurs in my development environment.

I am experiencing the same issue. If I use 'app.UseHangfireDashboard()' before calling 'BackgroundJob.Enqueue()', there are no errors. If I execute 'BackgroundJob.Enqueue()' before 'app.UseHangfireDashboard()', the exception is thrown. To fix this, I used the workaround mentioned on other responses: call 'app.Services.GetService();' before calling 'BackgroundJobEnqueue()'.