C# 没有为此对象定义无参数构造函数-Hangfire计划程序

C# 没有为此对象定义无参数构造函数-Hangfire计划程序,c#,scheduler,hangfire,C#,Scheduler,Hangfire,我刚刚在我的MVC网站上安装了Hangfire软件包。 我已经创建了一个Startup类 [assembly: OwinStartup(typeof(Website.Startup))] namespace Website { public partial class Startup { public void Configuration(IAppBuilder app) { Hangfire.ConfigureHangf

我刚刚在我的MVC网站上安装了Hangfire软件包。 我已经创建了一个Startup类

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

namespace Website
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            Hangfire.ConfigureHangfire(app);
            Hangfire.InitializeJobs();
        }
    }
}
还有一节绞刑课

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(), "* * * * *");
    }
}
因此,当我运行应用程序时,在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)

所以,我有点迷路了。我缺少什么?

您似乎没有将Hangfire连接到正在使用的IoC容器,因此它使用其默认策略创建请求的类型,在您的特定示例中,这意味着调用:

System.Activator.CreateInstance(typeof(CurrencyRatesJob));
由于
CurrencyRatesJob
类没有默认的无参数构造函数,因此此操作失败,并显示问题中显示的错误消息

要将Hangfire连接到IoC基础设施,您需要创建自己的
JobActivator
类,该类重写
ActivateJob
方法,并使用配置的IoC容器创建请求的作业类型的实例

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


该过程在中进行了描述,一些容器类型的标准实现可从中获得。我在这里发现了一个非常简单的讨论:

我将包括我的示例代码:

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);
IJob作业=NinjectWebCommon.Kernel.TryGet();
RecurringJob.AddOrUpdate(job.ToString(),()=>job.Run(model),Cron.Weekly,TimeZoneInfo.Utc);

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

Install-Package Hangfire.Unity
然后在Global.asax上注册,您将拥有bootstrapper初始化方法。导航到bootstrapper类,在初始化中有以下代码

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;
 }

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

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);
    }
}

你有hangfire应该使用什么类的注册码吗?我想我没有。我不记得读过那篇文章。你能告诉我更多吗?我不是一个hangfire专家,但很明显,它试图解决
CurrencyRatesJob
,但它不能,因为它不知道
iBudgetRepository
应该解决什么问题。这就是为什么会出现无空构造函数错误。也许这篇文章能有所帮助。@MarianEne检查我下面的回复,这会处理注入依赖项和您已经遇到的问题。我正在使用Ninject,我看到有一个Nuget包(Hangfire.Ninject),但我不确定如何使用它。我试图在作业中创建一个无参数构造函数,但它被执行了,但我没有IBudgetsRepository对象。@MarianEne,对,因为这正是您需要IoC解决的问题,它将实例注入到您的
CurrencyRatesJob
中,从而解决
IBudgetsRepository
依赖关系。使用这个Nuget包,您可以在启动时使用
usenjectActivator
扩展方法:
GlobalConfiguration.Configuration.usenjectActivator(内核)kernel
作为Ninject内核的实例进行编码。所以我有点困惑,我应该在哪里添加这个:
var kernel=new standardcenell();GlobalConfiguration.Configuration.UseinjectActivator(内核)。如果我将它添加到Global.asax,我会得到一个
Ninject.ActivationException
;如果我将它添加到作业中,就在
try
之后,我会得到相同的parameterless异常。@MarianEne,您提到您已经在项目中使用了Ninject,因此我假设您的MVC项目启动代码中已经创建了
内核
实例。如果您正在使用“NinjectMvc”Nuget软件包,您应该在
App_Start
文件夹中找到一个
NinjectWebCommon.cs
文件。在其
private static void RegisterServices(IKernel内核)
方法中,添加行
GlobalConfiguration.Configuration.useinjectactivator(内核)因此,我将具有Hangfire配置的类从Hangfire重命名为HangfireConfig,因为在使用UseInjectActivator时存在一些冲突。然后我移动了
Hangfire.GlobalConfiguration.Configuration.usenjectActivator(内核)
到我的
NinjectWebCommon
类,在这里我有内核,我使用它将接口绑定到适当的类。现在我收到另一个错误,即
Hangfire.IGlobalConfiguration
不包含
useinjectactivator
的定义。
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));