I have a specific use case (one time call from powershell script when new api version is installed) where I need to call a registered task that get some data and then create scheduled tasks (which are deleted later).
Wherever I try to register it, execution fails because it cannot resolve either the IBackgroundJobs or other command or IDbConnectionFactory dependencies.
I have tried something similar from what I see in ConfigureDbMigrations inside the ConfigureBackgroundJobs or AppHost without luck.
By the way, I think this doc snippet might not be correct, task aren’t registered when done in the afterAppHostInit
public class ConfigureDbMigrations : IHostingStartup
{
public void Configure(IWebHostBuilder builder) => builder
.ConfigureAppHost(afterAppHostInit:appHost => {
var migrator = new Migrator(appHost.Resolve<IDbConnectionFactory>(), typeof(Migration1000).Assembly);
AppTasks.Register("migrate", _ => migrator.Run());
AppTasks.Register("migrate.revert", args => migrator.Revert(args[0]));
AppTasks.Run();
});
}
Here is a small snippet of my test task inside the x.ConfigureAppHost
AppTasks.Register("test", (args) => {
Console.WriteLine("This is a test");
try
{
var jobs = appHost.Resolve<IBackgroundJobs>();
jobs.ThrowIfNull("IBackgroundJobs is not initialized. Cannot continue.");
var r = jobs.GetJob(9999); //should return null and not throw
if (r == null)
Console.WriteLine("GetJob(9999) returned null as expected");
// Create the recurring jobs
var request = new OrderCloseoutHrmRequest {
Id = 9999,
IsManualApproval = false,
UserName = "System"
};
jobs.RecurringCommand<OrderCloseoutHrmCommand>(OrderCloseoutHrmCommandNames.CreateNameForId(request.Id),Schedule.Interval(TimeSpan.FromSeconds(60)),request, new BackgroundJobOptions {
Tag = $"9999-TEST",
});
Console.WriteLine("Created or updated recurring Order Closeout HRM job for process order id: 99999");
}
catch (Exception e)
{
Console.WriteLine(e);
}
});
The AppTasks should only run after the IOC is built so it should have access to all your registered dependencies. I’ve just tried it locally and was able to verify that IBackgroundJobs was able to be resolved.
Have you tried debugging to see if the Plugins are registered and IOC is configured before the AppTask is run? Also how are you registering the plugins?
Will do more debugging soon and update the case. Did you try to create a Recurring Command once you have your job resolved? In my test, I can also resolve it but calls to GetJob or RecurringCommand will fail.
This is what I have: not too sure how to see if the Plugins are registered and all available…
public class ConfigureBackgroundJobs : IHostingStartup
{
public void Configure(IWebHostBuilder builder) => builder
.ConfigureServices((context,services) => {
services.AddPlugin(new CommandsFeature());
services.AddPlugin(new BackgroundsJobFeature());
services.AddHostedService<JobsHostedService>();
}).ConfigureAppHost(
(appHost) =>
{
var runner = new TaskRunner(appHost.Resolve<IBackgroundJobs>());
// this is NOT NULL
var jobFeature = appHost.AssertPlugin<BackgroundsJobFeature>();
jobFeature.ThrowIfNull("BackgroundsJobFeature is not registered in the AppHost. Cannot continue.");
// this is NOT NULL
var commandFeature = appHost.AssertPlugin<CommandsFeature>();
commandFeature.ThrowIfNull("CommandsFeature is not registered in the AppHost. Cannot continue.");
// this is empty
Console.WriteLine("Registered plugins are: {0}", appHost.PluginsLoaded?.Dump());
// jobFeature.CommandsFeature is NULL here. Is this normal?
jobFeature.CommandsFeature.ThrowIfNull("CommandsFeature is not registered in the BackgroundJobFeature. Cannot continue.");
AppTasks.Register("test", (args) =>
{
runner.SyncOCLTask(args);
});
});
}
public class JobsHostedService(ILogger<JobsHostedService> log, IBackgroundJobs jobs) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await jobs.StartAsync(stoppingToken);
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(3));
while (!stoppingToken.IsCancellationRequested && await timer.WaitForNextTickAsync(stoppingToken))
{
await jobs.TickAsync();
}
}
}
If I comment the ThrowIfNull, eventually, when the task runs and call the jobs.RecurringCommand , we end up in the BackgroundJobs.cs and we get the exception:
System.NullReferenceException: Object reference not set to an instance of an object.
at ServiceStack.Jobs.BackgroundJobs.AssertCommand(String command) in /home/runner/work/ServiceStack/ServiceStack/ServiceStack/src/ServiceStack.Jobs/BackgroundJobs.cs:line 1258
And if I set the jobFeature.CommandsFeature with the commandFeature, eventually, it will also fail but this time because if cannot resolve the sqllite connection
System.NullReferenceException: Object reference not set to an instance of an object.
at ServiceStack.OrmLite.OrmLiteConnectionFactoryExtensions.OpenDbConnection(IDbConnectionFactory connectionFactory, String namedConnection) in /home/runner/work/ServiceStack/ServiceStack/ServiceStack.OrmLite/src/ServiceStack.OrmLite/OrmLiteConnectionFactory.cs:line 270
at ServiceStack.Jobs.BackgroundsJobFeature.DefaultResolveAppDb(IDbConnectionFactory dbFactory) in /home/runner/work/ServiceStack/ServiceStack/ServiceStack/src/ServiceStack.Jobs/BackgroundsJobFeature.cs:line 104
at ServiceStack.Jobs.BackgroundsJobFeature.OpenDb() in /home/runner/work/ServiceStack/ServiceStack/ServiceStack/src/ServiceStack.Jobs/BackgroundsJobFeature.cs:line 121
at ServiceStack.Jobs.BackgroundJobs.CreateOrUpdate(ScheduledTask task) in /home/runner/work/ServiceStack/ServiceStack/ServiceStack/src/ServiceStack.Jobs/BackgroundJobs.ScheduledTasks.cs:line 31
at ServiceStack.Jobs.BackgroundJobs.RecurringCommand(String taskName, Schedule schedule, String commandName, Object arg, BackgroundJobOptions options) in /home/runner/work/ServiceStack/ServiceStack/ServiceStack/src/ServiceStack.Jobs/BackgroundJobs.ScheduledTasks.cs:line 66
at ServiceStack.Jobs.JobUtils.RecurringCommand[TCommand](IBackgroundJobs jobs, String taskName, Schedule schedule, Object request, BackgroundJobOptions options) in /home/runner/work/ServiceStack/ServiceStack/ServiceStack/src/ServiceStack/Jobs/JobUtils.cs:line 76
Yeah I can get a job and register a RecurringCommand in an AppTask, why are you not using .ConfigureAppHost(afterAppHostInit:appHost => ... as documented?
The AppHost and BackgroundJobs feature needs to be initialized before it can be used.
yeah, that was just to show you that it works only when not using afterAppHostInit. So, if I have this (with or without the AppTasks.Run() ), it works fine.
It runs the App Tasks if the App was launched with one, it should only be called once after all AppTasks have been registered. Since you can’t rely on the order ASP .NET Executes HostingStartup classes I’d recommend having all your AppTasks registered in one place before calling AppTasks.Run().