This seems to be a cleaner solution
[DependsOn(typeof(AbpQuartzModule))]
public class MyModule : AbpModule
{
public MyModule()
{
// patch configuration before PreInitialize.
// Do not use IocManager property in the constructor as it is not populated
Abp.Dependency.IocManager.Instance.IocContainer.Register(
Component
.For<IAbpQuartzConfiguration, QuartzStartupConfiguration>()
.LifestyleSingleton()
.IsDefault());
}
// ..
}
Hi, this registration does work, but from the logs I can confirm the suspect that 2 different schedulers are initialized:
...
INFO 2021-01-08 09:33:15,969 [1 ] Quartz.Impl.StdSchedulerFactory - Default Quartz.NET properties loaded from embedded resource file
INFO 2021-01-08 09:33:16,021 [1 ] Quartz.Core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: Quartz.Core.SchedulerSignalerImpl
INFO 2021-01-08 09:33:16,027 [1 ] Quartz.Core.QuartzScheduler - Quartz Scheduler v.3.0.7.0 created.
INFO 2021-01-08 09:33:16,028 [1 ] Quartz.Simpl.RAMJobStore - RAMJobStore initialized.
INFO 2021-01-08 09:33:16,030 [1 ] Quartz.Core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v3.0.7.0) 'DefaultQuartzScheduler' with instanceId 'NON_CLUSTERED'
Scheduler class: 'Quartz.Core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'Quartz.Simpl.DefaultThreadPool' - with 10 threads.
Using job-store 'Quartz.Simpl.RAMJobStore' - which does not support persistence. and is not clustered.
INFO 2021-01-08 09:33:16,034 [1 ] Quartz.Impl.StdSchedulerFactory - Quartz scheduler 'DefaultQuartzScheduler' initialized
INFO 2021-01-08 09:33:16,034 [1 ] Quartz.Impl.StdSchedulerFactory - Quartz scheduler version: 3.0.7.0
INFO 2021-01-08 09:33:16,034 [1 ] Quartz.Core.QuartzScheduler - JobFactory set to: Abp.Quartz.AbpQuartzJobFactory
WARN 2021-01-08 09:33:21,647 [1 ] tion.Repositories.EphemeralXmlRepository - Using an in-memory repository. Keys will not be persisted to storage.
WARN 2021-01-08 09:33:21,648 [1 ] taProtection.KeyManagement.XmlKeyManager - Neither user profile nor HKLM registry available. Using an ephemeral key repository. Protected data will be unavailable when application exits.
INFO 2021-01-08 09:33:24,436 [1 ] Quartz.Util.DBConnectionManager - Registering datasource 'UnoInsDataSource' with db provider: 'Quartz.Impl.AdoJobStore.Common.DbProvider'
INFO 2021-01-08 09:33:24,440 [1 ] Quartz.Impl.StdSchedulerFactory - Using object serializer: Quartz.Simpl.JsonObjectSerializer, Quartz.Serialization.Json
INFO 2021-01-08 09:33:25,304 [9 ] Quartz.Core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: Quartz.Core.SchedulerSignalerImpl
INFO 2021-01-08 09:33:25,305 [9 ] Quartz.Core.QuartzScheduler - Quartz Scheduler v.3.0.7.0 created.
INFO 2021-01-08 09:33:25,311 [9 ] Quartz.Impl.AdoJobStore.JobStoreTX - Detected usage of SqlServerDelegate - defaulting 'selectWithLockSQL' to 'SELECT * FROM QRTZ_LOCKS WITH (UPDLOCK,ROWLOCK) WHERE SCHED_NAME = 'Framework Scheduler' AND LOCK_NAME = @lockName'.
INFO 2021-01-08 09:33:25,311 [9 ] Quartz.Impl.AdoJobStore.JobStoreTX - Using db table-based data access locking (synchronization).
INFO 2021-01-08 09:33:25,314 [9 ] Quartz.Impl.AdoJobStore.JobStoreTX - JobStoreTX initialized.
INFO 2021-01-08 09:33:25,314 [9 ] Quartz.Core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v3.0.7.0) 'Framework Scheduler' with instanceId 'DEV-N00015.637456916052992244'
Scheduler class: 'Quartz.Core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'Quartz.Simpl.DefaultThreadPool' - with 10 threads.
Using job-store 'Quartz.Impl.AdoJobStore.JobStoreTX' - which supports persistence. and is clustered.
...
The Abp.Quartz
indeed creates a default scheduler regardless of what I do in my PreInitialize
public class AbpQuartzModule : AbpModule
{
public override void PreInitialize()
{
IocManager.Register<IAbpQuartzConfiguration, AbpQuartzConfiguration>();
// this line creates the default in-memory scheduler even if i replace the IAbpQuartzConfiguration in my PreInitialize
Configuration.Modules.AbpQuartz().Scheduler.JobFactory = new AbpQuartzJobFactory(IocManager);
}
For the moment I am going to shut the default one down before replacing it with my own.
Hi Zony, if I use the ReplaceService
the registred type is still overwritten by Abp.Quartz
and thus the IScheduler
is not properly configred. Am I missing something?
Furhermore, the Abp.Quartz.PreInitialize
calls indirectly the StdSchedulerFactory.GetDefaultScheduler()
, resulting in the creation of a default in-memory sheduler even before my custom PreInitialize
gets called. In this way I will end up with 2 different scheduler instances.
We need to configure Quartz to use SQL storage, but we can not store the connection string in the static quartz.config
file. In the AbpQuartzModule.PreInitialize()
the AbpQuartzConfiguration
is registred to resolve from the StdSchedulerFactory.GetDefaultScheduler()
, but we would need to register our own implementation of IAbpQuartzConfiguration
to create a scheduler with our configuration.
class AbpCustomQuartzConfiguration : IAbpQuartzConfiguration
{
public IScheduler Scheduler
{
get
{
var builder = SchedulerBuilder.Create();
builder.UsePersistentStore(options =>
{
options.UseClustering();
options.UseSqlServer(sqlServerOptions =>
{
sqlServerOptions.ConnectionString = "...";
});
options.UseSerializer<JsonObjectSerializer>();
});
builder.SchedulerId = StdSchedulerFactory.AutoGenerateInstanceId;
builder.MisfireThreshold = TimeSpan.FromMinutes(5);
var scheduler = builder.BuildScheduler().Result;
scheduler.JobFactory = new AbpQuartzJobFactory(IocManager.Instance);
scheduler.ListenerManager.AddJobListener(IocManager.Instance.Resolve<IJobListener>());
return scheduler;
}
}
}
How can we accomplish this?
Ok, I've created the issue
Thank you.
Hi @ismcagdas, I've made may attempts. After UseAbp is not possibile beacause the extension method AddMediatR(...) is on IServiceCollection that isn't available on Configure method.
After looking at the examples and what does this extesion https://github.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection, I ended up with this results:
Scenario: Framework and 2 plugin modules:
services.AddMediatR(typeof(Startup));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPreProcessorBehavior<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPostProcessorBehavior<,>));
IocManager.RegisterAssemblyByConvention(typeof(MyModule).GetAssembly());
IocManager.Register<IRequestHandler<PingCommand, string>, PingHandler>(DependencyLifeStyle.Transient); //Request/Response
IocManager.Register<IRequestHandler<PingCommandOneWay,Unit>, PingHandlerOneWay>(DependencyLifeStyle.Transient); //Request
IocManager.Register<INotificationHandler<PingNotification>, Pong1>(DependencyLifeStyle.Transient); //Notification
IocManager.Register<INotificationHandler<PingNotification>, Pong2>(DependencyLifeStyle.Transient); //Notification
//Pipelines
IocManager.IocContainer.Register(Component.For(typeof(IPipelineBehavior<,>)).ImplementedBy(typeof(LoggingBehavior<,>)).LifestyleTransient());
IocManager.IocContainer.Register(Component.For(typeof(IPipelineBehavior<,>)).ImplementedBy(typeof(ValidationBehaviour<,>)));
//Pre-Post processor IocManager.IocContainer.Register(Component.For(typeof(IRequestPreProcessor<>)).ImplementedBy(typeof(GenericPreProcessor<>)).LifestyleTransient());
IocManager.IocContainer.Register(Component.For(typeof(IRequestPostProcessor<,>)).ImplementedBy(typeof(GenericPostProcessor<,>)).LifestyleTransient());
//CatchAll notifications (See point no.3 for this)
IocManager.Register<INotificationHandler<INotification>, PongAll>(DependencyLifeStyle.Transient);
IocManager.IocContaine.Kernel.AddHandlersFilter(new ContravariantFilter());
public class ContravariantFilter : IHandlersFilter
{
public bool HasOpinionAbout(Type service)
{
if (!service.IsGenericType)
return false;
var genericType = service.GetGenericTypeDefinition();
var genericArguments = genericType.GetGenericArguments();
return genericArguments.Count() == 1
&& genericArguments.Single().GenericParameterAttributes.HasFlag(GenericParameterAttributes.Contravariant);
}
public IHandler[] SelectHandlers(Type service, IHandler[] handlers)
{
return handlers;
}
}
All this seems to works as expected, but is only a sort of cut&paste of AddMediatR with some Abp behaviours. I would be appreciate if you can officially integrate MediatR on Abp or at least validate how manually do.
Thank you very much.
Hi, I need to user the MediatR library but I have a problem with the registration of interfaces. My code is loaded through plugins directory so I need to manually discover the assemblies because are not referenced directly. In the ConfigureServices, just after services.AddAbp<... I have this code:
var pluginTypes = AppDomain.CurrentDomain.GetAssemblies() .AsParallel() .Where(a => a.FullName.StartsWith("MyDll.")) .SelectMany(x => x.GetTypes()) .ToArray(); services.AddMediatR(pluginTypes);
But at this point, my plugins aren't not loaded yet and so AddMediatR fails.
I do the same with SignalR but it works because is after app.UseAbp in Configure method.
So, how can I register my plugins to MediatoR ?
Thank you.
Ok @ismcagdas
I'll look more deeply in Hangfire configuration.
I suppose your are interested too about this topic and I'll need your technical support to evaluate eventually changes to apply to AspNetZero in order to support multi instances.
It'll take some time and so for the moment I close the ticket.
Hi, some news about this topic? Is very important for a cloud based installation.
Thank you.
But, wait a moment. Hangfire is integrated in Aspnet zero, so, two instance of aspnet zero has two instance of hangfire. The problem is still alive.