使用.net核心进行Hangfire依赖注入

时间:2017-01-24 13:52:58

标签: asp.net-core hangfire

如何在Hangfire中使用.net core的默认依赖注入?

我是Hangfire的新手,正在寻找一个适用于asp.net核心的例子。

8 个答案:

答案 0 :(得分:47)

请参阅GitHub上的完整示例https://github.com/gonzigonz/HangfireCore-Example 实时网站http://hangfirecore.azurewebsites.net/

  1. 确保您拥有Hangfire的核心版本:
    dotnet add package Hangfire.AspNetCore

  2. 通过定义JobActivator配置您的IoC。以下是与默认的asp.net核心容器服务一起使用的配置:

    public class HangfireActivator : Hangfire.JobActivator
    {
        private readonly IServiceProvider _serviceProvider;
    
        public HangfireActivator(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }
    
        public override object ActivateJob(Type type)
        {
            return _serviceProvider.GetService(type);
        }
    }  
    
  3. 接下来在Startup.ConfigureServices方法中将hangfire注册为服务:

    services.AddHangfire(opt => 
        opt.UseSqlServerStorage("Your Hangfire Connection string"));
    
  4. Startup.Configure方法中配置hangfire。与您的问题相关,是配置hangfire以使用我们刚才定义的新HangfireActivator。为此,您必须使用IServiceProvider提供hangfire,这可以通过将其添加到Configure方法的参数列表来实现。在运行时,DI将为您提供此服务:

    public void Configure(
        IApplicationBuilder app, 
        IHostingEnvironment env, 
        ILoggerFactory loggerFactory,
        IServiceProvider serviceProvider)
    {
        ...
    
        // Configure hangfire to use the new JobActivator we defined.
        GlobalConfiguration.Configuration
            .UseActivator(new HangfireActivator(serviceProvider));
    
        // The rest of the hangfire config as usual.
        app.UseHangfireServer();
        app.UseHangfireDashboard();
    }  
    
  5. 当您将作业排队时,请使用通常是您的界面的注册类型。除非您以这种方式注册,否则请勿使用具体类型。您必须使用在IoC注册的类型,否则Hang​​fire将无法找到它。 例如表示您已注册以下服务:

    services.AddScoped<DbManager>();
    services.AddScoped<IMyService, MyService>();
    
  6. 然后你可以将DbManager列入类的实例化版本:

        BackgroundJob.Enqueue(() => dbManager.DoSomething());
    

    但是你不能对MyService做同样的事情。使用实例化版本进行入队将失败,因为DI将失败,因为只有接口已注册。在这种情况下,你会这样排队:

        BackgroundJob.Enqueue<IMyService>( ms => ms.DoSomething());
    

答案 1 :(得分:11)

据我所知,您可以像使用任何其他服务一样使用.net核心依赖注入。

您可以使用包含要执行的作业的服务,可以像这样执行

var jobId = BackgroundJob.Enqueue(x => x.SomeTask(passParamIfYouWish));

以下是Job Service类的示例

public class JobService : IJobService
{
    private IClientService _clientService;
    private INodeServices _nodeServices;

    //Constructor
    public JobService(IClientService clientService, INodeServices nodeServices)
    {
        _clientService = clientService;
        _nodeServices = nodeServices;
    }

    //Some task to execute
    public async Task SomeTask(Guid subject)
    {
        // Do some job here
        Client client = _clientService.FindUserBySubject(subject);
    }      
}

在您的项目Startup.cs中,您可以正常添加依赖

services.AddTransient< IClientService, ClientService>();

不确定这是否回答了您的问题

答案 2 :(得分:5)

DoritoBandito的答案不完整或已弃用。

public class EmailSender {
     public EmailSender(IDbContext dbContext, IEmailService emailService)
     {
         _dbContext = dbContext;
         _emailService = emailService;
     }
}

注册服务:

services.AddTransient<IDbContext, TestDbContext>();
services.AddTransient<IEmailService, EmailService>();

入队:

BackgroundJob.Enqueue<EmailSender>(x => x.Send(13, "Hello!"));

来源: http://docs.hangfire.io/en/latest/background-methods/passing-dependencies.html

答案 3 :(得分:4)

此主题中的所有答案都是错误的/不完整的/过时的。这是一个使用ASP.NET Core 3.1和Hangfire.AspnetCore 1.7的示例。

客户:

//...
using Hangfire;
// ...

public class Startup
{
    // ...

    public void ConfigureServices(IServiceCollection services)
    {
        //...
        services.AddHangfire(config =>
        {
            // configure hangfire per your requirements
        });
    }
}

public class SomeController : ControllerBase
{
    private readonly IBackgroundJobClient _backgroundJobClient;

    public SomeController(IBackgroundJobClient backgroundJobClient)
    {
        _backgroundJobClient = backgroundJobClient;
    }
    
    [HttpPost("some-route")]
    public IActionResult Schedule([FromBody] SomeModel model)
    {
        _backgroundJobClient.Schedule<SomeClass>(s => s.Execute(model));
    }
}

服务器(相同或不同的应用程序):

{
    //...
    services.AddScoped<ISomeDependency, SomeDependency>();

    services.AddHangfire(hangfireConfiguration =>
    {
        // configure hangfire with the same backing storage as your client
    });
    services.AddHangfireServer();
}

public interface ISomeDependency { }
public class SomeDependency : ISomeDependency { }

public class SomeClass
{
    private readonly ISomeDependency _someDependency;

    public SomeClass(ISomeDependency someDependency)
    {
        _someDependency = someDependency;
    }

    // the function scheduled in SomeController
    public void Execute(SomeModel someModel)
    {

    }
}

答案 4 :(得分:0)

当前,Hangfire已与Asp.Net Core深度集成。将Hangfire.AspNetCore安装到set up the dashboard并自动进行DI集成。然后,您只需要像往常一样使用ASP.NET Core定义依赖项即可。

答案 5 :(得分:0)

我必须在主要功能中启动HangFire。这是我解决的方法:

public static void Main(string[] args)
    {
        var host = CreateWebHostBuilder(args).Build();
        using (var serviceScope = host.Services.CreateScope())
        {
            var services = serviceScope.ServiceProvider;

            try
            {
                var liveDataHelper = services.GetRequiredService<ILiveDataHelper>();
                var justInitHangfire = services.GetRequiredService<IBackgroundJobClient>();
                //This was causing an exception (HangFire is not initialized)
                RecurringJob.AddOrUpdate(() => liveDataHelper.RePopulateAllConfigDataAsync(), Cron.Daily());
                // Use the context here
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "Can't start " + nameof(LiveDataHelper));
            }
        }
        host.Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

答案 6 :(得分:0)

如果您尝试使用ASP.NET Core(在ASP.NET Core 2.2中进行了测试)快速设置Hangfire,则也可以使用Hangfire.MemoryStorage。可以在Startup.cs中进行所有配置:

using Hangfire;
using Hangfire.MemoryStorage;

public void ConfigureServices(IServiceCollection services) 
{
    services.AddHangfire(opt => opt.UseMemoryStorage());
    JobStorage.Current = new MemoryStorage();
}

protected void StartHangFireJobs(IApplicationBuilder app, IServiceProvider serviceProvider)
{
    app.UseHangfireServer();
    app.UseHangfireDashboard();

    //TODO: move cron expressions to appsettings.json
    RecurringJob.AddOrUpdate<SomeJobService>(
        x => x.DoWork(),
        "* * * * *");

    RecurringJob.AddOrUpdate<OtherJobService>(
        x => x.DoWork(),
        "0 */2 * * *");
}

public void Configure(IApplicationBuilder app, IServiceProvider serviceProvider)
{
    StartHangFireJobs(app, serviceProvider)
}

当然,所有内容都存储在内存中,一旦应用程序池被回收,它就会丢失,但是这是一种快速的方法,它可以确保在最少的配置下一切正常。

要切换到SQL Server数据库持久性,您应该安装Hangfire.SqlServer软件包,并对其进行简单配置,而不是配置内存存储:

services.AddHangfire(opt => opt.UseSqlServerStorage(Configuration.GetConnectionString("Default")));

答案 7 :(得分:0)

使用以下代码进行Hangfire配置

using eForms.Core;
using Hangfire;
using Hangfire.SqlServer;
using System;
using System.ComponentModel;
using System.Web.Hosting;

namespace eForms.AdminPanel.Jobs
{
    public class JobManager : IJobManager, IRegisteredObject
    {
        public static readonly JobManager Instance = new JobManager();
        //private static readonly TimeSpan ZeroTimespan = new TimeSpan(0, 0, 10);
        private static readonly object _lockObject = new Object();
        private bool _started;
        private BackgroundJobServer _backgroundJobServer;
        private JobManager()
        {
        }
        public int Schedule(JobInfo whatToDo)
        {
            int result = 0;
            if (!whatToDo.IsRecurring)
            {
                if (whatToDo.Delay == TimeSpan.Zero)
                    int.TryParse(BackgroundJob.Enqueue(() => Run(whatToDo.JobId, whatToDo.JobType.AssemblyQualifiedName)), out result);
                else
                    int.TryParse(BackgroundJob.Schedule(() => Run(whatToDo.JobId, whatToDo.JobType.AssemblyQualifiedName), whatToDo.Delay), out result);
            }
            else
            {
                RecurringJob.AddOrUpdate(whatToDo.JobType.Name, () => RunRecurring(whatToDo.JobType.AssemblyQualifiedName), Cron.MinuteInterval(whatToDo.Delay.TotalMinutes.AsInt()));
            }

            return result;
        }

        [DisplayName("Id: {0}, Type: {1}")]
        [HangFireYearlyExpirationTime]
        public static void Run(int jobId, string jobType)
        {
            try
            {
                Type runnerType;
                if (!jobType.ToType(out runnerType)) throw new Exception("Provided job has undefined type");
                var runner = runnerType.CreateInstance<JobRunner>();
                runner.Run(jobId);
            }
            catch (Exception ex)
            {
                throw new JobException($"Error while executing Job Id: {jobId}, Type: {jobType}", ex);
            }
        }

        [DisplayName("{0}")]
        [HangFireMinutelyExpirationTime]
        public static void RunRecurring(string jobType)
        {
            try
            {
                Type runnerType;
                if (!jobType.ToType(out runnerType)) throw new Exception("Provided job has undefined type");
                var runner = runnerType.CreateInstance<JobRunner>();
                runner.Run(0);
            }
            catch (Exception ex)
            {
                throw new JobException($"Error while executing Recurring Type: {jobType}", ex);
            }
        }

        public void Start()
        {
            lock (_lockObject)
            {
                if (_started) return;
                if (!AppConfigSettings.EnableHangFire) return;
                _started = true;
                HostingEnvironment.RegisterObject(this);
                GlobalConfiguration.Configuration
                    .UseSqlServerStorage("SqlDbConnection", new SqlServerStorageOptions { PrepareSchemaIfNecessary = false })
                   //.UseFilter(new HangFireLogFailureAttribute())
                   .UseLog4NetLogProvider();
                //Add infinity Expiration job filter
                //GlobalJobFilters.Filters.Add(new HangFireProlongExpirationTimeAttribute());

                //Hangfire comes with a retry policy that is automatically set to 10 retry and backs off over several mins
                //We in the following remove this attribute and add our own custom one which adds significant backoff time
                //custom logic to determine how much to back off and what to to in the case of fails

                // The trick here is we can't just remove the filter as you'd expect using remove
                // we first have to find it then save the Instance then remove it 
                try
                {
                    object automaticRetryAttribute = null;
                    //Search hangfire automatic retry
                    foreach (var filter in GlobalJobFilters.Filters)
                    {
                        if (filter.Instance is Hangfire.AutomaticRetryAttribute)
                        {
                            // found it
                            automaticRetryAttribute = filter.Instance;
                            System.Diagnostics.Trace.TraceError("Found hangfire automatic retry");
                        }
                    }
                    //Remove default hangefire automaticRetryAttribute
                    if (automaticRetryAttribute != null)
                        GlobalJobFilters.Filters.Remove(automaticRetryAttribute);
                    //Add custom retry job filter
                    GlobalJobFilters.Filters.Add(new HangFireCustomAutoRetryJobFilterAttribute());
                }
                catch (Exception) { }
                _backgroundJobServer = new BackgroundJobServer(new BackgroundJobServerOptions
                {
                    HeartbeatInterval = new System.TimeSpan(0, 1, 0),
                    ServerCheckInterval = new System.TimeSpan(0, 1, 0),
                    SchedulePollingInterval = new System.TimeSpan(0, 1, 0)
                });
            }
        }
        public void Stop()
        {
            lock (_lockObject)
            {
                if (_backgroundJobServer != null)
                {
                    _backgroundJobServer.Dispose();
                }
                HostingEnvironment.UnregisterObject(this);
            }
        }
        void IRegisteredObject.Stop(bool immediate)
        {
            Stop();
        }
    }
}

管理员职位经理

    public class Global : System.Web.HttpApplication
    {
        void Application_Start(object sender, EventArgs e)
        {
            if (Core.AppConfigSettings.EnableHangFire)
            {
                JobManager.Instance.Start();

                new SchedulePendingSmsNotifications().Schedule(new Core.JobInfo() { JobId = 0, JobType = typeof(SchedulePendingSmsNotifications), Delay = TimeSpan.FromMinutes(1), IsRecurring = true });

            }
        }
        protected void Application_End(object sender, EventArgs e)
        {
            if (Core.AppConfigSettings.EnableHangFire)
            {
                JobManager.Instance.Stop();
            }
        }
    }