bamotav / hangfire.recurringjobadmin Goto Github PK
View Code? Open in Web Editor NEWA dashboard to manage Hangfire's recurring jobs.
Home Page: https://www.nuget.org/packages/Hangfire.RecurringJobAdmin/
License: MIT License
A dashboard to manage Hangfire's recurring jobs.
Home Page: https://www.nuget.org/packages/Hangfire.RecurringJobAdmin/
License: MIT License
Validate the input Cron expression client-side using whatever external library javascript or Vuejs:
Or you can use Vue components:
https://vuejsexamples.com/vuetify-component-for-easier-editing-of-cron-expressions/
I am running into an issue with Xunit tests that are ran in parallel using the WebApplicationFactory. As the Startup is instantiated in parallel (4 threads concurrent), the PeriodicJobBuilder.GetAllJobs() method is running into concurrency issues with the assignment of the Metadata property.
System.InvalidOperationException : Collection was modified; enumeration operation may not execute.
at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
at Hangfire.RecurringJobAdmin.PeriodicJobBuilder.GetAllJobs()
at Hangfire.RecurringJobAdmin.ConfigurationExtensions.UseRecurringJobAdmin(IGlobalConfiguration config, Assembly[] assemblies)
PeriodicJobBuilder.Metadata = new List(); // I assume that if we change to use a temporary variable and make the assignment to the Metadata property at the end of the GetAllJobs method, the concurrency issues would be mitigated?
`
internal static class PeriodicJobBuilder
{
public static List Metadata { get; private set; }
internal static void GetAllJobs()
{
PeriodicJobBuilder.Metadata = new List<RecurringJobAttribute>();
foreach (Assembly assembly in StorageAssemblySingleton.GetInstance().currentAssembly)
{
foreach (Type type in assembly.GetTypes())
{
foreach (MethodInfo declaredMethod in type.GetTypeInfo().DeclaredMethods)
{
if (declaredMethod.IsDefined(typeof (RecurringJobAttribute), false))
{
RecurringJobAttribute customAttribute = declaredMethod.GetCustomAttribute<RecurringJobAttribute>(false);
if (customAttribute != null)
{
if (declaredMethod.GetCustomAttributes(true).OfType<RecurringJobAttribute>().Any<RecurringJobAttribute>())
PeriodicJobBuilder.Metadata.Add(declaredMethod.GetCustomAttribute<RecurringJobAttribute>());
new RecurringJobRegistry().Register(customAttribute.RecurringJobId, declaredMethod, customAttribute.Cron, string.IsNullOrEmpty(customAttribute.TimeZone) ? TimeZoneInfo.Utc : TimeZoneInfo.FindSystemTimeZoneById(customAttribute.TimeZone), customAttribute.Queue ?? "default");
}
}
}
}
}
}
`
Both button show the text "Invalid date", but the recurring job is executed serveral times without any error.
I am using TZ UTC and West european Standard time.
Any ideas what is going wrong?
Hi, Bryan.
Thank you for useful extension. I'm trying to use it but it doesn't start through the job configuration panel, although it runs well through "HangfireTaskScheduler" class. Here is what I'm entering to "Add new job" form:
Job Id : AppointmentNotificationJob
Cron : 0 2 * * *
Class : LAPICore.Infrastructure.TaskScheduler.Jobs.AppointmentNotificationJob
Method: Run
Queue: default
Maybe I entered the wrong class path, but I tried to enter different and "LAPICore.Infrastructure.TaskScheduler.Jobs" is that namespace in which this class is located.
What am I doing wrong?
Thank you in advance
Modify all the .csproject project files so that the project can build on OS Linux
Instead of using system times zones via TimeZoneInfo; ctor resolve and use Hangfire.Core.ITimeZoneResolver
public static DateTime ChangeTimeZone(this DateTime dateTime, string timeZoneId) => TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dateTime, timeZoneId);
Changes DisplayName timezone to property Id when is Linux or MacOs Operative System.
TimeZoneInfo.GetSystemTimeZones().Select(o => new Tuple<string, string>(o.Id, o.DisplayName));
To
TimeZoneInfo.GetSystemTimeZones().Select(o => new Tuple<string, string>(o.Id, o.Id));
Because when you display the timezone with the property Displayname you don't see the specified time-zone correctly for example when is MacOS:
When you use the Id on MacOS:
You need to define a contract in the csproject of the library :
And later use the preprocessor directives:
Running into the following errors as the PeriodicJobBuilder.GetAllJobs(); method is called and it loops through all classes within the assembly looking for the recurring job attributes. Is there a way to exclude/include types that would use the attribute rather than looking at all types?
Could not load type 'Microsoft.EntityFrameworkCore.Metadata.RelationalModelAnnotations' from assembly 'Microsoft.EntityFrameworkCore.Relational, Version=3.1.8.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.
Could not load type 'Microsoft.EntityFrameworkCore.Metadata.RelationalPropertyAnnotations' from assembly 'Microsoft.EntityFrameworkCore.Relational, Version=3.1.8.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.
Could not load type 'Microsoft.EntityFrameworkCore.Metadata.Internal.RelationalFullAnnotationNames' from assembly 'Microsoft.EntityFrameworkCore.Relational, Version=3.1.8.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. <s:Microsoft.AspNetCore.Hosting.Diagnostics>
System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types.
Could not load type 'Microsoft.EntityFrameworkCore.Metadata.RelationalModelAnnotations' from assembly 'Microsoft.EntityFrameworkCore.Relational, Version=3.1.8.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.
Could not load type 'Microsoft.EntityFrameworkCore.Metadata.RelationalPropertyAnnotations' from assembly 'Microsoft.EntityFrameworkCore.Relational, Version=3.1.8.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.
Could not load type 'Microsoft.EntityFrameworkCore.Metadata.Internal.RelationalFullAnnotationNames' from assembly 'Microsoft.EntityFrameworkCore.Relational, Version=3.1.8.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.
at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
at System.Reflection.RuntimeModule.GetTypes()
at Hangfire.RecurringJobAdmin.PeriodicJobBuilder.GetAllJobs()
at Hangfire.RecurringJobAdmin.ConfigurationExtensions.UseRecurringJobAdmin(IGlobalConfiguration config, Assembly assembly)
Hi
I've just want to ask how can I inject dependency in the constructor because it always calls to the non-parameter constructor.
When the application is stopped running and started again, all changes saved in the database are lost
I have 8 recurring jobs and they show fine in the "Recurring Jobs" tab. In the Job Configuration tab, 8 rows display but they are mostly blank with the exception of "Last Execution" and "Next Execution". Even those are incorrect as they show the current time. XHR GetJobs request has all the job data but it's not getting rendered. Console error as well. Package version 1.0.5. Hangfire.Core version 1.76.16 with Mongo storage.
vue:6 TypeError: Cannot read properties of null (reading 'length') at wn.eval (eval at Ya (vue:6), <anonymous>:3:3901) at wn.e._render (vue:6) at wn.r (vue:6) at fn.get (vue:6) at new fn (vue:6) at vue:6 at wn.$mount (vue:6) at wn.$mount (vue:6) at wn.t._init (vue:6) at new wn (vue:6)
When creating a job take the default TimeZone of the machine
Job Configuration page does not display any jobs.
Broken by this commit 8f46030
file GetJobDispatcher, line 36
Allow to make a backup of the job configuration and make a restore of it.
I set the job execute at 1:00 AM every day ,and it started at 1;00AM but executed 10 times interval 1 minute
The client side job form has to be validated before making the post.
I am running ASP.NET Core on .NET 5 preview 1.
In Startup.cs is have this:
public class Startup
{
public void Configure(
IApplicationBuilder app,
IWebHostEnvironment env,
IRecurringJobManager recurringJobs)
{
....
}
}
This worked fine with 0.1.5.
With the latest version (0.1.6) I get this exception.
> 2020-09-21 02:33:51.600 +00:00 [Critical] Microsoft.AspNetCore.Hosting.Diagnostics: Application startup exception
System.Exception: Could not resolve a service of type 'Hangfire.IRecurringJobManager' for the parameter 'recurringJobs' of method 'Configure' on type 'NetProxy.Startup'.
---> System.BadImageFormatException: Bad IL format. The format of the file 'D:\home\site\wwwroot\api-ms-win-core-console-l1-1-0.dll' is invalid.
at System.Runtime.Loader.AssemblyLoadContext.LoadFromPath(IntPtr ptrNativeAssemblyLoadContext, String ilPath, String niPath, ObjectHandleOnStack retAssembly)
at System.Runtime.Loader.AssemblyLoadContext.LoadFromAssemblyPath(String assemblyPath)
at System.Reflection.Assembly.LoadFile(String path)
at Hangfire.RecurringJobAdmin.Core.StorageAssemblySingleton.<>c__DisplayClass7_0.<SetCurrentAssembly>b__2(String path)
at System.Collections.Generic.List`1.ForEach(Action`1 action)
at Hangfire.RecurringJobAdmin.Core.StorageAssemblySingleton.SetCurrentAssembly(Assembly assembly)
at Hangfire.RecurringJobAdmin.ConfigurationExtensions.UseRecurringJobAdmin(IGlobalConfiguration config, Assembly assembly)
at NetProxy.Startup.<ConfigureServices>b__8_5(IGlobalConfiguration config) in /home/vsts/work/1/s/NetProxy/Startup.cs:line 164
at Hangfire.HangfireServiceCollectionExtensions.<>c__DisplayClass0_0.<AddHangfire>b__0(IServiceProvider provider, IGlobalConfiguration config)
at Hangfire.HangfireServiceCollectionExtensions.<>c__DisplayClass1_0.<AddHangfire>b__7(IServiceProvider serviceProvider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Hangfire.HangfireServiceCollectionExtensions.<>c__DisplayClass6_0`1.<TryAddSingletonChecked>b__0(IServiceProvider serviceProvider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
--- End of inner exception stack trace ---
at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
at Microsoft.AspNetCore.Hosting.ConfigureBuilder.<>c__DisplayClass4_0.<Build>b__0(IApplicationBuilder builder)
at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass15_0.<UseStartup>b__1(IApplicationBuilder app)
at Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterBuilderStartupFilter.<>c__DisplayClass0_0.<Configure>g__MiddlewareFilterBuilder|0(IApplicationBuilder builder)
at Microsoft.ApplicationInsights.AspNetCore.ApplicationInsightsStartupFilter.<>c__DisplayClass2_0.<Configure>b__0(IApplicationBuilder app)
at Microsoft.AspNetCore.Server.IIS.Core.IISServerSetupFilter.<>c__DisplayClass2_0.<Configure>b__0(IApplicationBuilder app)
at Microsoft.AspNetCore.HostFilteringStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder app)
at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken)
Notice the call to Hangfire.RecurringJobAdmin.Core.StorageAssemblySingleton.SetCurrentAssembly(Assembly assembly);
When we update any property of a job or create a new job and the job method has parameters, this does not work, because the ReflectionHelper.InvokeVoidMethod (job.Class, job.Method)
method does not set the parameters that the method needs to.
We need to change this way of updating the Job to another, we can use the RecurringJobRegistry class, this is in charge of looking for the classes that are in the assembly that was given by the application that uses the plug-in this registers or updates the new Job in Hangfire.
This is the way :
var addOrUpdate = Expression.Call(
typeof (RecurringJob),
nameof (RecurringJob.AddOrUpdate),
new type [] {method.DeclaringType},
new expression []
{
Expression.Constant (recurringJobId),
Expression.Lambda (methodCall, x),
Expression.Constant (cron),
Expression.Constant (timeZone),
Expression.Constant (queue)
});
Expression.Lambda (addOrUpdate).Compile().DynamicInvoke ();
Could you add parameters to the job definition view?
Ideally these could be discovered via reflection of the type.method input; something like swagger UI.
I try to describe a job that should be executed every 10 minutes, shouldn't be executed if already running, and shouldn't be retried:
[RecurringJob("*/10 * * * *", "default", RecurringJobId = "ThisService")]
[DisableConcurrentlyJobExecution(nameof(DoThis))]
[AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Fail)]
public async Task DoThis(PerformContext ctx)
{
throw new Exception();
}
This code always ends up deleting the task, instead of marking it as failed.
The reason seems to be the check at https://github.com/bamotav/Hangfire.RecurringJobAdmin/blob/master/src/Hangfire.RecurringJobAdmin/Attributes/DisableConcurrentlyJobExecutionAttribute.cs
... && !context.CandidateState.IsFinal
The state Failed is not marked as IsFinal (see https://github.com/HangfireIO/Hangfire/blob/master/src/Hangfire.Core/States/FailedState.cs), so the task is deleted instead of being reported as expected.
As a workaround I'm deploying my own attribute which checks for this particular state.
I am getting following errors when migrating to 3.1 from 2.2
`Unhandled exception. System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types.
Could not load type 'Microsoft.AspNetCore.Http.Authentication.AuthenticationManager' from assembly 'Microsoft.AspNetCore.Http.Abstractions, Version=3.1.19.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.
Could not load type 'Microsoft.AspNetCore.Http.Authentication.AuthenticateInfo' from assembly 'Microsoft.AspNetCore.Http.Abstractions, Version=3.1.19.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.
Could not load type 'Microsoft.AspNetCore.Http.Features.Authentication.ChallengeBehavior' from assembly 'Microsoft.AspNetCore.Http.Features, Version=3.1.19.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.
at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
at System.Reflection.RuntimeModule.GetTypes()
at System.Reflection.Assembly.GetTypes()
at Hangfire.RecurringJobAdmin.PeriodicJobBuilder.GetAllJobs()
at Hangfire.RecurringJobAdmin.ConfigurationExtensions.UseRecurringJobAdmin(IGlobalConfiguration config, Boolean includeReferences, Assembly[] assemblies)
`
Hi, great plugin, awesome job :)
I'm loading dll's dynamically.
I'm not referencing them in the main project.
When i try to use the dashboard to change the cron i'm getting
note: i'm using .UseRecurringJobAdmin(true,typeof(Startup).Assembly) in the main class, but like i said, I'm not referencing the loaded dll's in the main project...
any workaround for this?
thanks
Add option to remove Recurring Job tab.
Hello,
I show an issues in the columns next execution and last execution. I need your help with that.
The Configuration job and the datetime in Next Execution and Last Execution aren't exact. For example: In the image, whether we see the job (id:CloseMenuForDayBreakfast) the configuration cron is 0 8 * * 0-6
, whereby the next execution must is the 2020-09-24 8:00, but the next execution in the column is in 2020-09-24 12:00.
My timezone is America/Santo_Domingo
[RecurringJob("0 8 * * 0-6", "America/Santo_Domingo", "default", RecurringJobId = "CloseMenuForDayBreakfast")]
public async Task CloseMenuForDayBreakfast()
{
//body code
}
I think that is the timezone configuration of plugin that doesn't get my configuration timezone.
Use moment js for the dates columns of grids.
The idea is are values of the columns say like that the grid original of recurrings jobs grid:
The original grid uses a razor server-side to render this value in the columns but you need to replicate this logic on the client-side using Vuejs.
You can check the file HtmlHelper:
For injecting another external library:
1- Add the file to the folder:
2- Right-click the file and select the options property and changes build action to Embedded resource and the second option in the same display changes to Copy always :
3: Add the resources to the Dashboard route:
In case of having a selected timezone and publishing on a server that does not have this timezone, look for the closest one to what is installed on the server.
I'm running into MIME type issues running this extension in Firefox.
It looks like it was fixed in this commit: b48369d
Any idea when this is going to be released?
Add icon button to remove a job.
Here's how the page looks like here:
The error while trying to parse dates is:
vue:6 TypeError: Cannot read property 'length' of null
at wn.eval (eval at Ya (vue:6), :3:3901)
at wn.e._render (vue:6)
at wn.r (vue:6)
at fn.get (vue:6)
at new fn (vue:6)
at vue:6
at wn.$mount (vue:6)
at wn.$mount (vue:6)
at wn.t._init (vue:6)
at new wn (vue:6)
I have a conflict between two libraries, this one require
Newtonsoft.Json (>= 12.0.2)
and the other one
Newtonsoft.Json (>= 10.0.0 && < 11.0.0)
Since it require a DLL less than v11 may you please check if Newtonsoft.Json >= 10.0.3 is ok.
Create a detail screen of the jobs
Hi, @bamotav
I have this issue again with 1.0.0 RecurringJobAdmin and 1.7.17 Hangfire versions installed. Doesn't matter what class name I'm putting: full path or not.
In addition to that any error is forcing to add all job parameters again. This is really annoying behavior, could you fix it?
Thank you in advance
Convert the class name input to a search engine, which allows searching all the references of the project.
Add a search engine to filter the job table
Add Icon button to trigger a job.
You should see a column to express the duration of a task.
The TimeZones input should work as a search engine to be able to write the timezone that you want to search in the list offered by the select.
Good day,
Can we please add a new overload method to the ConfigurationExtensions.cs for MongoDB storage?
public static MongoStorage UserRecurringJobAdmin(this MongoStorage mongoStorage, [NotNull] params Assembly[] assemblies)
{
if(assemblied == null) throw new ArgumenrtNullException(nameof(assemblies);
StorageAssemblySingleton.GetInstance().SetCurrentAssembly(assemblies: assemblies);
PeriodicJobBuilder.GetAllJobs();
CreateManagmentJob();
return mongoStorage;
}
I am willing to help with the code. We really want to use this extension and by adding this overload we can make it work with the MongoStorage. We cannot use SqlStorage becasue our DBAs will not allow all the commands that hangfire issue.
Regards
Bennie
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.