6

我刚刚在我的 MVC 网站中安装了 Hangfire 包。我创建了一个 Startup 类

[assembly: OwinStartup(typeof(Website.Startup))]

namespace Website
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            Hangfire.ConfigureHangfire(app);
            Hangfire.InitializeJobs();
        }
    }
}

和一个 Hangfire 课程

public class Hangfire
{
    public static void ConfigureHangfire(IAppBuilder app)
    {
        app.UseHangfire(config =>
        {
            config.UseSqlServerStorage("DefaultConnection");
            config.UseServer();
            config.UseAuthorizationFilters(); 
        });
    }

    public static void InitializeJobs()
    {
        RecurringJob.AddOrUpdate<CurrencyRatesJob>(j => j.Execute(), "* * * * *");
    }
}

另外,我在一个单独的类库中创建了一个新工作

public class CurrencyRatesJob
{
    private readonly IBudgetsRepository budgetsRepository;

    public CurrencyRatesJob(IBudgetsRepository budgetsRepository)
    {
        this.budgetsRepository = budgetsRepository;
    }

    public void Execute()
    {
        try
        {
            var budgets = new BudgetsDTO();
            var user = new UserDTO();

            budgets.Sum = 1;
            budgets.Name = "Hangfire";
            user.Email = "email@g.com";

            budgetsRepository.InsertBudget(budgets, user);
        }
        catch (Exception ex)
        {
            var message = ex.ToString();
            throw new NotImplementedException(message);
        }
    }
}

因此,当我运行应用程序时,在 Hangfire 的仪表板中,我收到以下错误:

Failed An exception occurred during job activation.
System.MissingMethodException

No parameterless constructor defined for this object.

System.MissingMethodException: No parameterless constructor defined for this object.
   at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
   at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic)
   at System.Activator.CreateInstance(Type type)
   at Hangfire.JobActivator.ActivateJob(Type jobType)
   at Hangfire.Common.Job.Activate(JobActivator activator)

所以,我在这里有点迷路了。我错过了什么?

4

4 回答 4

5

您似乎尚未将 Hangfire 连接到您正在使用的 IoC 容器,因此它使用其默认策略来创建请求的类型,在您的具体示例中意味着调用:

System.Activator.CreateInstance(typeof(CurrencyRatesJob));

因为CurrencyRatesJob该类没有默认的无参数构造函数,所以这会失败并显示您在问题中显示的错误消息。

要将 Hangfire 连接到您的 IoC 基础架构,您需要创建自己的JobActivator类来覆盖该ActivateJob方法并使用配置的 IoC 容器来创建请求的作业类型的实例。

可以在此处找到使用 Unity 作为容器 ( UnityJobActivator)的示例,可以在此处找到Funq容器 ( )的示例。FunqJobActivator

Hangfire 文档中描述了该过程, Hangfire github repo提供了几种容器类型的标准实现

于 2015-05-04T17:56:41.937 回答
1

您需要注入依赖项才能使其正常工作。安装 nuget unity 包:

Install-Package Hangfire.Unity

然后在 Global.asax 上注册,您将拥有 BootStraper 初始化方法。导航到引导程序类并在初始化时具有以下代码,

DependencyResolver.SetResolver(new UnityDependencyResolver(container));

如果您使用 Unity,完整的代码将如下所示。

public static class Bootstrapper
{
    public static IUnityContainer Initialise()
    {
        var container = BuildUnityContainer();

        DependencyResolver.SetResolver(new UnityDependencyResolver(container));

        return container;
     }



 private static IUnityContainer BuildUnityContainer()
 {
    var container = new UnityContainer();       
    GlobalConfiguration.Configuration.UseUnityActivator(container);
    RegisterTypes(container);
    return container;
 }
于 2017-09-14T08:51:56.363 回答
0

我在这里找到了一个非常简单的讨论:Hangfire Discussion

我将包括我的示例代码:

public class Job : IJob
{
    private readonly IService _service;
    private readonly IDbContext _context;

    public Job()
    {
         // this needs to be here, although this won't be used in the actual running
    }

    public Job(IService service, IDbContext context) : this()
    {
        _service = service;
        _context = context;
    }

    public override void Run(SomeModel searchLocationModel)
    {
    }
}

我对 Hangfire 的实际调用如下:

IJob job = NinjectWebCommon.Kernel.TryGet<Job>();

RecurringJob.AddOrUpdate(job.ToString(), () => job.Run(model), Cron.Weekly, TimeZoneInfo.Utc);
于 2016-04-09T20:54:36.880 回答
0

以上答案都无法在我们的项目中实施。所以我们最终创建了一个后台作业助手,它使用反射来实例化类(没有无参数构造函数),然后调用该方法。

using Newtonsoft.Json.Linq;
using System;
using System.Reflection;

public static class BackgroundJobHelper
{
    public static object Execute(string userId, Type classType, string functionName, object[] param)
    {
        ServiceFactory serviceFactory = new ServiceFactory(userId);
        var classToInvoke = Activator.CreateInstance(classType, new object[] { serviceFactory });
        return Send(classType, classToInvoke, functionName, param);
    }

    private static object Send(Type classType, object className, string functionName, object[] param, Type[] fnParameterTypes = null)
    {
        MethodInfo methodInfo;
        if (!fnParameterTypes.IsNullOrEmpty())
        {
            methodInfo = classType.GetMethod(functionName, fnParameterTypes);
        }
        else
        {
            methodInfo = classType.GetMethod(functionName);
        }
        var methodParameters = methodInfo.GetParameters();
        //Object of type 'System.Int64' cannot be converted to type 'System.Int32'. While deserializing int is converted into long hence explictly make it Int32.
        for (int i = 0; i < param.Length; i++)
        {
            var methodParameterType = methodParameters[i].ParameterType;
            if (param[i] != null)
            {
                if (param[i] is long l)
                {
                    if (l >= int.MinValue && l <= int.MaxValue) param[i] = (int)l;
                }
                else if (param[i].GetType() == typeof(JObject))
                {
                    param[i] = (param[i] as JObject).ToObject(methodParameterType);
                }
                else if (param[i].GetType() == typeof(JArray))
                {
                    param[i] = (param[i] as JArray).ToObject(methodParameterType);
                }
            }
        }
        return methodInfo.Invoke(className, param);
    }
}

用法:

var backgroundJob = new BackgroundJobClient(new SqlServerStorage(db.Database.Connection));
var result = backgroundJob.Schedule(() => BackgroundJobHelper.Execute(userId, this.GetType(), nameof(this.SendMailAsync), new object[] { projectId, remarks }), TimeSpan.FromSeconds(30));
于 2020-01-15T16:49:29.750 回答