Hangfire.Topshelf
Best practice for hangfire samples
Install / Use
/learn @icsharp/Hangfire.TopshelfREADME
Hangfire.Topshelf
To initialize this repo, you need to run git command git submodule update --init --recursive after you clone the master repo.
Samples as below:
Host Hangfire server in windows service using Topshelf
- Impl interface
ServiceControlbased onOWIN.
/// <summary>
/// OWIN host
/// </summary>
public class Bootstrap : ServiceControl
{
private readonly LogWriter _logger = HostLogger.Get(typeof(Bootstrap));
private IDisposable webApp;
public string Address { get; set; }
public bool Start(HostControl hostControl)
{
try
{
webApp = WebApp.Start<Startup>(Address);
return true;
}
catch (Exception ex)
{
_logger.Error($"Topshelf starting occured errors:{ex.ToString()}");
return false;
}
}
public bool Stop(HostControl hostControl)
{
try
{
webApp?.Dispose();
return true;
}
catch (Exception ex)
{
_logger.Error($"Topshelf stopping occured errors:{ex.ToString()}");
return false;
}
}
}
- Extension method
UseOwin
public static HostConfigurator UseOwin(this HostConfigurator configurator, string baseAddress)
{
if (string.IsNullOrEmpty(baseAddress)) throw new ArgumentNullException(nameof(baseAddress));
configurator.Service(() => new Bootstrap { Address = baseAddress });
return configurator;
}
- Start windows service
static int Main(string[] args)
{
log4net.Config.XmlConfigurator.Configure();
return (int)HostFactory.Run(x =>
{
x.RunAsLocalSystem();
x.SetServiceName(HangfireSettings.ServiceName);
x.SetDisplayName(HangfireSettings.ServiceDisplayName);
x.SetDescription(HangfireSettings.ServiceDescription);
x.UseOwin(baseAddress: HangfireSettings.ServiceAddress);
x.SetStartTimeout(TimeSpan.FromMinutes(5));
//https://github.com/Topshelf/Topshelf/issues/165
x.SetStopTimeout(TimeSpan.FromMinutes(35));
x.EnableServiceRecovery(r => { r.RestartService(1); });
});
}
Using IoC with Autofac
- Register components using
Autofac.Module
/// <summary>
/// Hangfire Module
/// </summary>
public class HangfireModule : Autofac.Module
{
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
{
base.AttachToComponentRegistration(componentRegistry, registration);
// Handle constructor parameters.
registration.Preparing += OnComponentPreparing;
// Handle properties.
registration.Activated += (sender, e) => InjectLoggerProperties(e.Instance);
}
private void InjectLoggerProperties(object instance)
{
var instanceType = instance.GetType();
// Get all the injectable properties to set.
// If you wanted to ensure the properties were only UNSET properties,
// here's where you'd do it.
var properties = instanceType
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.PropertyType == typeof(ILog) && p.CanWrite && p.GetIndexParameters().Length == 0);
// Set the properties located.
foreach (var propToSet in properties)
{
propToSet.SetValue(instance, LogProvider.GetLogger(instanceType), null);
}
}
private void OnComponentPreparing(object sender, PreparingEventArgs e)
{
e.Parameters = e.Parameters.Union(new[]
{
new ResolvedParameter(
(p, i) => p.ParameterType == typeof(ILog),
(p, i) => LogProvider.GetLogger(p.Member.DeclaringType)
),
});
}
/// <summary>
/// Auto register
/// </summary>
/// <param name="builder"></param>
protected override void Load(ContainerBuilder builder)
{
//register all implemented interfaces
builder.RegisterAssemblyTypes(ThisAssembly)
.Where(t => typeof(IDependency).IsAssignableFrom(t) && t != typeof(IDependency) && !t.IsInterface)
.AsImplementedInterfaces();
//register speicified types here
builder.Register(x => new RecurringJobService());
}
}
- Extension method
UseAutofac
public static IContainer UseAutofac(this IAppBuilder app, HttpConfiguration config)
{
if (config == null) throw new ArgumentNullException(nameof(config));
var builder = new ContainerBuilder();
var assembly = typeof(Startup).Assembly;
builder.RegisterAssemblyModules(assembly);
builder.RegisterApiControllers(assembly);
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.UseAutofacActivator(container);
return container;
}
Register RecurringJob automatically
- Usage
public class RecurringJobService
{
[RecurringJob("*/1 * * * *")]
[DisplayName("InstanceTestJob")]
[Queue("jobs")]
public void InstanceTestJob(PerformContext context)
{
context.WriteLine($"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")} InstanceTestJob Running ...");
}
[RecurringJob("*/5 * * * *")]
[DisplayName("JobStaticTest")]
[Queue("jobs")]
public static void StaticTestJob(PerformContext context)
{
context.WriteLine($"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")} StaticTestJob Running ...");
}
}
public interface ISampleService : IAppService
{
/// <summary>
/// simple job test
/// </summary>
/// <param name="context"></param>
[RecurringJob("0 4 1 * *")]
[AutomaticRetry(Attempts = 3)]
[DisplayName("SimpleJobTest")]
[Queue("jobs")]
void SimpleJob(PerformContext context);
}
In app start, using extension method UseRecurringJob to assign the types targeted by RecurringJobAttribute:
GlobalConfiguration.Configuration.UseRecurringJob(typeof(RecurringJobService), typeof(ISampleService))
Architecture
Monolithic

Cluster

Related Skills
node-connect
349.7kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
109.7kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
349.7kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
349.7kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
