MassTransit.QuartzIntegration.ScheduledMessageJob:无法实例化没有空构造函数的类型(参数'ScheduledMessageJob')'
Posted
技术标签:
【中文标题】MassTransit.QuartzIntegration.ScheduledMessageJob:无法实例化没有空构造函数的类型(参数\'ScheduledMessageJob\')\'【英文标题】:MassTransit.QuartzIntegration.ScheduledMessageJob: Cannot instantiate type which has no empty constructor (Parameter 'ScheduledMessageJob')'MassTransit.QuartzIntegration.ScheduledMessageJob:无法实例化没有空构造函数的类型(参数'ScheduledMessageJob')' 【发布时间】:2021-11-18 18:19:16 【问题描述】:目前正在将一个项目移植到 .Net 5,我一直在努力解决 MassTransit 和 Quartz 调度程序配置。
未安排的消息可以正常工作,但安排的消息却不行。
我能够在 Quartz DB 中看到计划的作业,但是在自动触发作业时出现以下错误:
Quartz.SchedulerException: Problem instantiating class 'MassTransit.QuartzIntegration.ScheduledMessageJob: Cannot instantiate type which has no empty constructor (Parameter 'ScheduledMessageJob')'
在这个项目中有
一个网络应用程序,应该发送消息和预定消息。 一个 Windows 服务 (BackgroundService),应该使用消息并发送消息(非预定消息)。网站
在 startup.cs 我有:
NameValueCollection properties = new();
properties.Add("quartz.scheduler.instanceName", "QuartzSchedulerInstance1");
properties.Add("quartz.scheduler.instanceId", "Quartz_instance_1");
properties.Add("quartz.threadPool.type", "Quartz.Simpl.SimpleThreadPool, Quartz");
properties.Add("quartz.threadPool.threadCount", "10");
properties.Add("quartz.threadPool.threadPriority", "2");
properties.Add("quartz.jobStore.misfireThreshold", "60000");
properties.Add("quartz.jobStore.type", "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz");
properties.Add("quartz.jobStore.useProperties", "true");
properties.Add("quartz.jobStore.dataSource", "default");
properties.Add("quartz.jobStore.tablePrefix", "QRTZ_");
properties.Add("quartz.jobStore.lockHandler.type", "Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz");
properties.Add("quartz.dataSource.default.connectionString", "Server=(local);Database=Quartz;Trusted_Connection=True;");
properties.Add("quartz.dataSource.default.provider", "SqlServer");
properties.Add("quartz.serializer.type", "json");
StdSchedulerFactory factory = new StdSchedulerFactory(properties);
var scheduler = factory.GetScheduler().Result;
services.AddSingleton(scheduler);
services.AddMassTransit(x =>
x.AddMessageScheduler(busSettings.QuartzQueueAddress);
x.UsingRabbitMq((context, cfg) =>
cfg.Host(busSettings.RabbitMQAddress, host =>
host.Username(busSettings.RabbitMQLogin);
host.Password(busSettings.RabbitMQPassword);
);
cfg.UseMessageScheduler(busSettings.QuartzQueueAddress);
cfg.ConfigureEndpoints(context);
);
);
services.AddMassTransitHostedService();
以这种方式发送非预定消息:
var message = new BuildReminderDeploySetRequest(Guid.NewGuid(), ... other parameters...);
var target = await _bus.GetSendEndpoint(_busSettings.ReminderRequestQueueAddress);
await target.Send(message);
虽然预定的发送是这样的:
var message = new BuildReminderDeploySetRequest(Guid.NewGuid(), ... other parameters...);
await _scheduler.ScheduleSend(_busSettings.ReminderRequestQueueAddress, reminderDate, message);
分别通过 DI 注入以下内容: IBusControl _bus 用于非预定消息 IMessageScheduler _scheduler 用于预定消息。
Windows 服务
在 Program.cs 我有:
services.AddTransient<ProcessBuildReminderDeploySetConsumer>();
NameValueCollection properties = new();
properties.Add("quartz.scheduler.instanceName", "QuartzSchedulerInstance1");
properties.Add("quartz.scheduler.instanceId", "Quartz_instance_1");
properties.Add("quartz.threadPool.type", "Quartz.Simpl.SimpleThreadPool, Quartz");
properties.Add("quartz.threadPool.threadCount", "10");
properties.Add("quartz.threadPool.threadPriority", "2");
properties.Add("quartz.jobStore.misfireThreshold", "60000");
properties.Add("quartz.jobStore.type", "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz");
properties.Add("quartz.jobStore.useProperties", "true");
properties.Add("quartz.jobStore.dataSource", "default");
properties.Add("quartz.jobStore.tablePrefix", "QRTZ_");
properties.Add("quartz.jobStore.lockHandler.type", "Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz");
properties.Add("quartz.dataSource.default.connectionString", "Server=(local);Database=Quartz;Trusted_Connection=True;");
properties.Add("quartz.dataSource.default.provider", "SqlServer");
properties.Add("quartz.serializer.type", "json");
StdSchedulerFactory factory = new StdSchedulerFactory(properties);
var scheduler = factory.GetScheduler().Result;
scheduler.Start();
services.AddSingleton(scheduler);
services.AddMassTransit(conf =>
conf.AddMessageScheduler(busSettings.QuartzQueueAddress);
conf.UsingRabbitMq((context, cfg) =>
cfg.Host(busSettings.RabbitMQAddress, host =>
host.Username(busSettings.RabbitMQLogin);
host.Password(busSettings.RabbitMQPassword);
);
cfg.UseMessageScheduler(busSettings.QuartzQueueAddress);
cfg.ReceiveEndpoint(busSettings.QuartzQueueName, epc =>
var partitioner = cfg.CreatePartitioner(16);
epc.Consumer(() =>
new ScheduleMessageConsumer(scheduler),
x => x.Message<ScheduleMessage>(
m => m.UsePartitioner(
partitioner,
p => p.Message.CorrelationId)
)
);
epc.Consumer(
() => new CancelScheduledMessageConsumer(scheduler),
x => x.Message<CancelScheduledMessage>(
m => m.UsePartitioner(partitioner, p => p.Message.TokenId)
)
);
cfg.UseMessageScheduler(epc.InputAddress);
cfg.Durable = true;
// THIS USED TO BE IN THE OLD .NET 4.6 PROJECT
// BUT THOSE CLASSES DO NOT EXIST ANYMORE
//var specification =
//new SchedulerBusFactorySpecification(scheduler, epc.InputAddress);
//cfg.AddBusFactorySpecification(specification);
);
cfg.ReceiveEndpoint(busSettings.ReminderRequestQueueName, e =>
// Enforce loading only 1 message at a time for now
e.PrefetchCount = 1;
e.UseMessageRetry(r =>
r.Interval(retryCount: 5, interval: TimeSpan.FromMilliseconds(500));
);
e.Consumer<ProcessBuildReminderDeploySetConsumer>(context);
);
cfg.UseMessageScheduler(busSettings.QuartzQueueAddress);
cfg.ConfigureEndpoints(context);
);
);
services.AddMassTransitHostedService();
使用直接发送到队列的消息没有问题。 关于定时消息,由于 Quartz 没有正确触发,因此无法消费。
我确信可能有一些配置问题导致了这个错误,但是在不同的配置方式之间+版本之间的变化我无法弄清楚。
【问题讨论】:
【参考方案1】:在当前版本的 Quartz 中,有extension methods for configuring the service collection。这些方法将ISchedulerFactory
添加到容器中。
然后,在您想要托管石英的过程中,您可以在 MassTransit 中使用配置中的非显而易见的扩展方法对其进行配置:
services.AddMassTransit(conf =>
conf.AddMessageScheduler(busSettings.QuartzQueueAddress);
conf.UsingRabbitMq((context, cfg) =>
cfg.Host(busSettings.RabbitMQAddress, host =>
host.Username(busSettings.RabbitMQLogin);
host.Password(busSettings.RabbitMQPassword);
);
cfg.UseInMemoryScheduler(context, busSettings.QuartzQueueName)
这将使用 Quartz 配置的调度程序工厂(显然,您可以将其配置为不在内存中。它还配置总线观察器以在总线启动/停止时自动启动/停止石英。
【讨论】:
谢谢你,根据你的指示,我能够让它工作:)!以上是关于MassTransit.QuartzIntegration.ScheduledMessageJob:无法实例化没有空构造函数的类型(参数'ScheduledMessageJob')'的主要内容,如果未能解决你的问题,请参考以下文章