diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln
index 309be85ea1..991d1d08d5 100644
--- a/framework/Volo.Abp.sln
+++ b/framework/Volo.Abp.sln
@@ -372,6 +372,7 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AzureServiceBus", "src\Volo.Abp.AzureServiceBus\Volo.Abp.AzureServiceBus.csproj", "{808EC18E-C8CC-4F5C-82B6-984EADBBF85D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EventBus.Azure", "src\Volo.Abp.EventBus.Azure\Volo.Abp.EventBus.Azure.csproj", "{FB27F78E-F10E-4810-9B8E-BCD67DCFC8A2}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Authorization.Abstractions", "src\Volo.Abp.Authorization.Abstractions\Volo.Abp.Authorization.Abstractions.csproj", "{87B0C2A8-FE95-4779-8B9C-2181AA52B3FA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.TextTemplating.Core", "src\Volo.Abp.TextTemplating.Core\Volo.Abp.TextTemplating.Core.csproj", "{184E859A-282D-44D7-B8E9-FEA874644013}"
@@ -393,10 +394,13 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EventBus.Boxes", "src\Volo.Abp.EventBus.Boxes\Volo.Abp.EventBus.Boxes.csproj", "{6E289F31-7924-418B-9DAC-62A7CFADF916}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.DistributedLocking", "src\Volo.Abp.DistributedLocking\Volo.Abp.DistributedLocking.csproj", "{9A7EEA08-15BE-476D-8168-53039867038E}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Auditing.Contracts", "src\Volo.Abp.Auditing.Contracts\Volo.Abp.Auditing.Contracts.csproj", "{508B6355-AD28-4E60-8549-266D21DBF2CF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Http.Client.Web", "src\Volo.Abp.Http.Client.Web\Volo.Abp.Http.Client.Web.csproj", "{F7407459-8AFA-45E4-83E9-9BB01412CC08}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BackgroundWorkers.Hangfire", "src\Volo.Abp.BackgroundWorkers.Hangfire\Volo.Abp.BackgroundWorkers.Hangfire.csproj", "{E5FCE710-C5A3-4F94-B9C9-BD1E99252BFB}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -1187,6 +1191,10 @@ Global
{F7407459-8AFA-45E4-83E9-9BB01412CC08}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F7407459-8AFA-45E4-83E9-9BB01412CC08}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F7407459-8AFA-45E4-83E9-9BB01412CC08}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E5FCE710-C5A3-4F94-B9C9-BD1E99252BFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E5FCE710-C5A3-4F94-B9C9-BD1E99252BFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E5FCE710-C5A3-4F94-B9C9-BD1E99252BFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E5FCE710-C5A3-4F94-B9C9-BD1E99252BFB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1388,6 +1396,7 @@ Global
{9A7EEA08-15BE-476D-8168-53039867038E} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{508B6355-AD28-4E60-8549-266D21DBF2CF} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{F7407459-8AFA-45E4-83E9-9BB01412CC08} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
+ {E5FCE710-C5A3-4F94-B9C9-BD1E99252BFB} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}
diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/FodyWeavers.xml b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/FodyWeavers.xml
new file mode 100644
index 0000000000..bc5a74a236
--- /dev/null
+++ b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/FodyWeavers.xsd b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/FodyWeavers.xsd
new file mode 100644
index 0000000000..3f3946e282
--- /dev/null
+++ b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo.Abp.BackgroundWorkers.Hangfire.csproj b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo.Abp.BackgroundWorkers.Hangfire.csproj
new file mode 100644
index 0000000000..fe87db9cd1
--- /dev/null
+++ b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo.Abp.BackgroundWorkers.Hangfire.csproj
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ netstandard2.0
+ Volo.Abp.BackgroundWorkers.Hangfire
+ Volo.Abp.BackgroundWorkers.Hangfire
+ $(AssetTargetFallback);portable-net45+win8+wp8+wpa81;
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/AbpBackgroundWorkersHangfireModule.cs b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/AbpBackgroundWorkersHangfireModule.cs
new file mode 100644
index 0000000000..4efbdcffb7
--- /dev/null
+++ b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/AbpBackgroundWorkersHangfireModule.cs
@@ -0,0 +1,36 @@
+using System;
+using Hangfire;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Volo.Abp.Hangfire;
+using Volo.Abp.Modularity;
+
+namespace Volo.Abp.BackgroundWorkers.Hangfire
+{
+ [DependsOn(
+ typeof(AbpBackgroundWorkersModule),
+ typeof(AbpHangfireModule))]
+ public class AbpBackgroundWorkerHangfireModule : AbpModule
+ {
+ public override void OnPreApplicationInitialization(ApplicationInitializationContext context)
+ {
+ var options = context.ServiceProvider.GetRequiredService>().Value;
+ if (!options.IsEnabled)
+ {
+ var hangfireOptions = context.ServiceProvider.GetRequiredService>().Value;
+ hangfireOptions.BackgroundJobServerFactory = CreateOnlyEnqueueJobServer;
+ }
+ }
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddSingleton(typeof(HangfirePeriodicBackgroundWorkerAdapter<>));
+ }
+
+ private BackgroundJobServer CreateOnlyEnqueueJobServer(IServiceProvider serviceProvider)
+ {
+ serviceProvider.GetRequiredService();
+ return null;
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerBase.cs b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerBase.cs
new file mode 100644
index 0000000000..d5de154909
--- /dev/null
+++ b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerBase.cs
@@ -0,0 +1,11 @@
+using System.Threading.Tasks;
+
+namespace Volo.Abp.BackgroundWorkers.Hangfire
+{
+ public abstract class HangfireBackgroundWorkerBase : BackgroundWorkerBase, IHangfireBackgroundWorker
+ {
+ public string CronExpression { get; set; }
+
+ public abstract Task ExecuteAsync();
+ }
+}
diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerManager.cs b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerManager.cs
new file mode 100644
index 0000000000..a1a3db2969
--- /dev/null
+++ b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerManager.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+using Hangfire;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Threading;
+
+namespace Volo.Abp.BackgroundWorkers.Hangfire
+{
+ [Dependency(ReplaceServices = true)]
+ public class HangfireBackgroundWorkerManager : IBackgroundWorkerManager, ISingletonDependency
+ {
+ public Task StartAsync(CancellationToken cancellationToken = new CancellationToken())
+ {
+ return Task.CompletedTask;
+ }
+
+ public Task StopAsync(CancellationToken cancellationToken = new CancellationToken())
+ {
+ return Task.CompletedTask;
+ }
+
+ public void Add(IBackgroundWorker worker)
+ {
+ if (worker is IHangfireBackgroundWorker hangfireBackgroundWorker)
+ {
+ RecurringJob.AddOrUpdate(() => hangfireBackgroundWorker.ExecuteAsync(),
+ hangfireBackgroundWorker.CronExpression);
+ }
+ else
+ {
+ int? period;
+
+ if (worker is AsyncPeriodicBackgroundWorkerBase or PeriodicBackgroundWorkerBase)
+ {
+ var timer = (AbpTimer) worker.GetType()
+ .GetProperty("Timer", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(worker);
+ period = timer?.Period;
+ }
+ else
+ {
+ return;
+ }
+
+ if (period == null)
+ {
+ return;
+ }
+
+ var adapterType = typeof(HangfirePeriodicBackgroundWorkerAdapter<>).MakeGenericType(worker.GetType());
+ var workerAdapter = Activator.CreateInstance(adapterType) as IHangfireBackgroundWorker;
+
+ RecurringJob.AddOrUpdate(() => workerAdapter.ExecuteAsync(), GetCron(period.Value));
+ }
+ }
+
+ protected virtual string GetCron(int period)
+ {
+ var time = TimeSpan.FromMilliseconds(period);
+ string cron;
+
+ if (time.TotalSeconds <= 59)
+ {
+ cron = $"*/{time.TotalSeconds} * * * * *";
+ }
+ else if (time.TotalMinutes <= 59)
+ {
+ cron = $"*/{time.TotalMinutes} * * * *";
+ }
+ else if (time.TotalHours <= 23)
+ {
+ cron = $"0 */{time.TotalHours} * * *";
+ }
+ else
+ {
+ cron = $"0 0 */{time.TotalDays} * *";
+ }
+
+ return cron;
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfirePeriodicBackgroundWorkerAdapter.cs b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfirePeriodicBackgroundWorkerAdapter.cs
new file mode 100644
index 0000000000..c9e3b202eb
--- /dev/null
+++ b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfirePeriodicBackgroundWorkerAdapter.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Volo.Abp.BackgroundWorkers.Hangfire
+{
+ public class HangfirePeriodicBackgroundWorkerAdapter : HangfireBackgroundWorkerBase
+ where TWorker : IBackgroundWorker
+ {
+ private readonly MethodInfo _doWorkAsyncMethod;
+ private readonly MethodInfo _doWorkMethod;
+
+ public HangfirePeriodicBackgroundWorkerAdapter()
+ {
+ _doWorkAsyncMethod =
+ typeof(TWorker).GetMethod("DoWorkAsync", BindingFlags.Instance | BindingFlags.NonPublic);
+ _doWorkMethod = typeof(TWorker).GetMethod("DoWork", BindingFlags.Instance | BindingFlags.NonPublic);
+ }
+
+ public override async Task ExecuteAsync()
+ {
+ var workerContext = new PeriodicBackgroundWorkerContext(ServiceProvider);
+ var worker = ServiceProvider.GetRequiredService();
+
+ switch (worker)
+ {
+ case AsyncPeriodicBackgroundWorkerBase asyncPeriodicBackgroundWorker:
+ await (Task) _doWorkAsyncMethod.Invoke(asyncPeriodicBackgroundWorker, new object[] {workerContext});
+ break;
+ case PeriodicBackgroundWorkerBase periodicBackgroundWorker:
+ _doWorkMethod.Invoke(periodicBackgroundWorker, new object[] {workerContext});
+ break;
+ }
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/IHangfireBackgroundWorker.cs b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/IHangfireBackgroundWorker.cs
new file mode 100644
index 0000000000..19653a7091
--- /dev/null
+++ b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/IHangfireBackgroundWorker.cs
@@ -0,0 +1,13 @@
+using System.Threading.Tasks;
+
+namespace Volo.Abp.BackgroundWorkers.Hangfire
+{
+ public interface IHangfireBackgroundWorker : IBackgroundWorker
+ {
+ string CronExpression { get; set; }
+
+ Task ExecuteAsync();
+ }
+}
+
+
diff --git a/nupkg/common.ps1 b/nupkg/common.ps1
index 7d48c1ac1d..fd99c6a068 100644
--- a/nupkg/common.ps1
+++ b/nupkg/common.ps1
@@ -73,6 +73,7 @@ $projects = (
"framework/src/Volo.Abp.BackgroundJobs.Quartz",
"framework/src/Volo.Abp.BackgroundWorkers",
"framework/src/Volo.Abp.BackgroundWorkers.Quartz",
+ "framework/src/Volo.Abp.BackgroundWorkers.Hangfire",
"framework/src/Volo.Abp.BlazoriseUI",
"framework/src/Volo.Abp.BlobStoring",
"framework/src/Volo.Abp.BlobStoring.FileSystem",