我刚刚在我的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)
所以,我在这里有点失落。我错过了什么?
答案 0 :(得分:5)
您似乎没有将Hangfire连接到您正在使用的IoC容器,因此它使用其默认策略来创建请求的类型,在您的特定示例中表示调用:
System.Activator.CreateInstance(typeof(CurrencyRatesJob));
由于CurrencyRatesJob
类没有默认的无参数构造函数,因此失败并显示您在问题中显示的错误消息。
要将Hangfire连接到IoC基础架构,您需要创建自己的JobActivator
类来覆盖ActivateJob
方法,并使用配置的IoC容器创建所请求作业类型的实例。
可以找到使用Unity作为容器(UnityJobActivator
)的示例here,并找到Funq
容器(FunqJobActivator
)的示例{{3 }}
here中描述了该过程,Hangfire documentation
提供了几种容器类型的标准实现。答案 1 :(得分:1)
您需要注入依赖项才能使其正常工作。 安装nuget unity包:
Install-Package Hangfire.Unity
然后在Global.asax上注册你将拥有BootStraper初始化方法。导航到启动strapper类并在初始化中有以下代码,
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;
}
答案 2 :(得分: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);
答案 3 :(得分: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));