Please go through the below scenario. This is based on C# .Net Web API.
I have to call GetMonthlyData() which is located in BL layer. BL Layer related dependencies are initialized in the Controller and it works fine.
It is required to call the same GetMonthlyData() as a Job and I am using Quartz for that. However when initiating the Job I am getting an issue since the dependencies are not resolved at that time since the call does not goes through the controller. How can I resolve this issue?
FileProcessController.cs -> This is the controller which initiates the Dependency Injection
[RoutePrefix("api/FileProcess")]
public class FileProcessController : ApiController
{
private readonly IFileProcessManager _fileProcessManager;
public FileProcessController(IFileProcessManager fileProcessManager)
{
this._fileProcessManager = fileProcessManager;
}
[HttpGet]
[Route("MontlyData")]
public MonthlyProcessDataDM GetMonthlyData()
{
try
{
var result = this._fileProcessManager.GetMonthlyData();
return result;
}
catch (Exception ex)
{
throw ExceptionStatusCode.ThrowHttpErrorResponseAndMessage(ex);
}
}
}
}
FileProcessManager.cs -> This contains the implementation of GetMonthlyData()
public class FileProcessManager : IFileProcessManager
{
public FileProcessManager(IFileProcessManagerRepo ifileProcessManagerRepo)
{
}
public MonthlyProcessDataDM GetMonthlyData()
{
//Code to be executed
}
}
Interface IFileProcessManager
public interface IFileProcessManager
{
MonthlyProcessDataDM GetMonthlyData();
}
Execution of the above flow works properly. In the next scenario, I need to call GetMonthlyData() using Quartz. For that
Global.asax.cs
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
System.Web.Http.GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
JobScheduler.Start(); // This is the scheduler for the above mentioned job.
}
JobScheduler.cs
public class JobScheduler
{
public static void Start()
{
IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler();
scheduler.Start();
IJobDetail job = JobBuilder.Create<MonthlyProcessManager>().Build(); // This is the Implementation
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.StartNow()
.WithSimpleSchedule(x => x
.WithIntervalInSeconds(10)
.RepeatForever())
.Build();
scheduler.ScheduleJob(job, trigger);
}
}
I tried to implement as below to resolve the dependency injection, However it throws null reference exception pointing the dependency injection code.
MonthlyProcessManager.cs
public class MonthlyProcessManager : Quartz.IJob
{
public IFileProcessManager _fileProcessManager;
public MonthlyProcessManager(IFileProcessManager fileProcessManager)
{
_fileProcessManager = fileProcessManager;
}
public void Execute(Quartz.IJobExecutionContext context)
{
MonthlyProcessDataDM monthlyProcessDataVM = _fileProcessManager.GetMonthlyData();
}
}
How can I resolve this issue. Thanks in advance.
Related
I have configured Quartz in my .Net program and I have a strange issue happening that I need help with.
What happens is that, if I put the ActuationService with the constructor commented the Quartz implementation works showing the "Hello message. Otherwise, if I have the ActuationService with the constructor method (like in the code below) the message "Hello" does not show
I'm using dependency injection for the Interfaces in the constructor method and I have the following code:
What am I doing wrong ?
using Quartz;
using Quartz.Impl;
using Quartz.Logging;
using Microsoft.Extensions.Logging;
using AAA.Service.Actuations;
namespace AAA.Service.StartUppers
{
public class StartUpService : IStartUpService
{
private readonly IScheduler _quartzScheduler;
public StartUpService(IScheduler quartzScheduler)
{
this._quartzScheduler = quartzScheduler;
}
public async void startTasks()
{
try
{
IJobDetail deviceActuation = JobBuilder.Create<ActuationService>()
.WithIdentity("deviceActuator", "group2")
.Build();
ITrigger triggerDeviceActuation = TriggerBuilder.Create()
.WithIdentity("triggerDeviceActuation", "group2")
.WithCronSchedule("0 0/1 * * * ?")
.Build();
await _quartzScheduler.ScheduleJob(deviceActuation, triggerDeviceActuation);
await _quartzScheduler.Start();
}
catch (Exception e)
{
throw new System.Exception("An error ocurred - " + e.Message);
}
}
}
}
And the class where I have the execute method
namespace AAA.Service.Actuations
{
public class ActuationService : IJob
{
private readonly IUserRepository _userRepo;
private readonly IDeviceRepository _deviceRepo;
private readonly IMeasurementAdapter _measurementsAdapter;
public ActuationService( IUserRepository userRepo, IDeviceRepository deviceRepo, IMeasurementAdapter measurementsAdapter)
{
this._userRepo = userRepo;
this._deviceRepo = deviceRepo;
this._measurementsAdapter = measurementsAdapter;
}
public async Task Execute(IJobExecutionContext context)
{
Console.WriteLine("HELLO");
}
}
}
Qaurtz doesn't know how to instantiate IUserRepository, IDeviceRepository, IMeasurementAdapter. To use DI in Quartz, you must use the Quartz.Extensions.DependencyInjection package.
Call UseMicrosoftDependencyInjectionJobFactory in Quartz configuration:
public void ConfigureServices(IServiceCollection services)
{
//Your services...
services.AddQuartz(q =>
{
q.UseMicrosoftDependencyInjectionJobFactory();
//Your configuration
}
}
Check out the documentation.
I have managed to configure scoped services together with scoped filters for consumers, meaning that I can set a value to a scoped service in a filter implementing IFilter<ConsumeContext<T>> and registering the filter with UseConsumeFilter. The filter sets a value in my scoped service and after that the scoped service can be injected into my consumer and still have the value set.
I have tried to do the same thing for activities using IFilter<ExecuteContext<TArguments>> and registering my filter with UseExecuteActivityFilter.
The values set in the ExecuteActivityContext are not reachable in the Activity. I think they become two different DI scopes. I'll share the code from my activity and consumer implementations and maybe there is something missing in the activity one. I have tried to only keep the important part so if there is illegal syntax somewhere it's from me trying to clean up the code for SO.
Is this me using DI in a wrong way or something thats bugged with DI for activities? I tried following the "Scoped Filters" documentation on masstransits website. I'm on .net core 3.1 and masstransit 7.0.4.
Scoped service used for testing
//Interface
public interface IContextService
{
string TenantId { get; set; }
}
//DI registration
services.AddScoped<IContextService, ContextService>();
Activity configuration, this is not working
//Filter
public class RetreiveContextExecuteFilter<TArguments> : IFilter<ExecuteContext<TArguments>>
where TArguments : class
{
public IContextService _contextService { get; }
public RetreiveContextExecuteFilter(IContextService contextService)
{
_contextService = contextService;
}
public async Task Send(ExecuteContext<TArguments> context, IPipe<ExecuteContext<TArguments>> next)
{
_contextService.tenantId = "test-tenant";
await next.Send(context);
}
public void Probe(ProbeContext context)
{
var scope = context.CreateFilterScope("testcontextinformation");
}
}
//Activity
public class ExampleActivity
: IExecuteActivity<ExampleActivityArguments>
{
private readonly IContextService _contextService;
public ExampleActivity(IContextService contextService)
{
_contextService = contextService;
}
public async Task<ExecutionResult> Execute(ExecuteContext<ExampleActivityArguments> context)
{
var tenant = _contextService.tenantId; //Empty
}
}
//DI
services.AddMassTransit(cfg =>
{
cfg.AddActivitiesFromNamespaceContaining<ExampleActivity>();
services.TryAddSingleton(KebabCaseEndpointNameFormatter.Instance);
cfg.UsingRabbitMq(ConfigureBus);
});
private static void ConfigureBus(IBusRegistrationContext context, IRabbitMqBusFactoryConfigurator configurator)
{
configurator.ConfigureEndpoints(context);
configurator.UseExecuteActivityFilter(typeof(RetreiveContextExecuteFilter<>), context);
}
Consumer configuration, this is working
//Filter definition
public class RetreiveContextConsumeFilter<T> : IFilter<ConsumeContext<T>>
where T : class
{
public IContextService _contextService { get; }
public RetreiveContextConsumeFilter(IContextService contextService)
{
_contextService = contextService;
}
public Task Send(ConsumeContext<T> context, IPipe<ConsumeContext<T>> next)
{
_contextService.TenantId = "test tenant";
return next.Send(context);
}
public void Probe(ProbeContext context)
{
context.CreateFilterScope("contextinformation");
}
}
//Consumer
public class ExampleConsumer
: IConsumer<ExampleEvent>
{
private readonly IContextService _contextService;
public ExampleConsumer(IContextService contextService)
{
_contextService = contextService;
}
public async Task Consume(ConsumeContext<ExampleEvent> context)
{
var id = _contextService.TenantId(); //Correct value
}
}
//DI
services.AddMassTransit(cfg =>
{
cfg.AddConsumersFromNamespaceContaining<ExampleConsumer>();
services.TryAddSingleton(KebabCaseEndpointNameFormatter.Instance);
cfg.UsingRabbitMq(ConfigureBus);
});
private static void ConfigureBus(IBusRegistrationContext context, IRabbitMqBusFactoryConfigurator configurator)
{
configurator.ConfigureEndpoints(context);
configurator.UseConsumeFilter(typeof(RetreiveContextConsumeFilter<>), context);
}
First guess, is that your configuration order is incorrect. MassTransit builds pipelines, and you are configuring your endpoints before the filter, which is going to make the filter run after the endpoints. That's my guess.
Change the consumer to:
configurator.UseConsumeFilter(typeof(RetreiveContextConsumeFilter<>), context);
configurator.ConfigureEndpoints(context);
Change the activity to:
configurator.UseExecuteActivityFilter(typeof(RetreiveContextExecuteFilter<>), context);
configurator.ConfigureEndpoints(context);
I have a task that needs to be run in a separate thread in the background, and I am using SignalR to report progress. This worked some time ago, and I had made some code modifications, but I am at a complete loss as to the error I receive now:
"No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested. This generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario.) Under the web integration always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container itself."
Any help is greatly appreciated!
public ActionResult DoAction(IEnumerable<string> items){
//...
Func<CancellationToken, Task> taskFunc = CancellationToken => performAction(items);
HostingEnvironment.QueueBackgroundWorkItem(taskFunc);
//...
}
private async Task performAction(IEnumerable<string> items){
var svc = AutofacDependencyResolver.Current.AppicationContainer.BeginLifetimeScope().Resolve<MyService>();
svc.Method(items);
}
public class MyService{
private EntityContext db;
public MyService(EntityContext db){
this.db = db;
}
}
In my Startup.Container.cs file:
builder.RegisterType<MyService>().As<MyService>().InstancePerLifetimeScope();
builder.RegisterType<EntityContext>().InstancePerRequest();
I recently implemented something similar using help from this answer and this answer. You need to create a new lifetime scope - it sounds like your doing this in a web application, so you need to create the scope via the per-request tag (example below).
Another (non-StackOverflow) answer provides similar advice.
public Task Run<T>(Action<T> action)
{
Task.Factory.StartNew(() =>
{
using (var lifetimeScope = _container.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag))
{
var service = lifetimeScope.Resolve<T>();
action(service);
}
});
return Task.FromResult(0);
}
I did something similar to #Chima Osuji but I think something is off in his answer so I'm gonna describe my solution and explain it.
public class BackgroundTaskFactory : IBackgroundTaskFactory
{
private ILifetimeScope lifetimeScope;
public BackgroundTaskFactory(ILifetimeScope lifetimeScope)
{
this.lifetimeScope = lifetimeScope;
}
public Task Run<T>(Action<T> action)
{
Task task = Task.Factory.StartNew(() =>
{
using (var lifetimeScope = this.lifetimeScope.BeginLifetimeScope())
{
var service = lifetimeScope.Resolve<T>();
action(service);
}
});
return task;
}
}
It's important to point out that my Run method is returning the task that was created on Task.Factory.StartNew. That way someone waits for the result, he gets the right task. In the other solutions they are returning Task.FromResult(0) which returns a dummy task.
BeginLifetimeScope creates a new scope as a child of the injected scope. If the injected scope is an InstancePerLifetimeScope associated to a web request, as soon as the web request scope is disposed, this new scope will also be disposed and it will error out. Child scopes cannot live longer than its parent scopes. Solution? Register BackgroundTaskFactory as singleton. When you do that, the injected lifetime scope will be the root scope, which doesn't get disposed until the app is disposed.
containerBuilder.RegisterType< BackgroundTaskFactory >()
.As< IBackgroundTaskFactory >()
.SingleInstance();
An updated answer based on the code above:
Usage:
public class ServiceModule :Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<AutoFac.AsyncRunner>().As<AutoFac.IAsyncRunner>().SingleInstance();
}
}
public class Controller
{
private AutoFac.IAsyncRunner _asyncRunner;
public Controller(AutoFac.IAsyncRunner asyncRunner)
{
_asyncRunner = asyncRunner;
}
public void Function()
{
_asyncRunner.Run<IService>((cis) =>
{
try
{
//do stuff
}
catch
{
// catch stuff
throw;
}
});
}
}
The Interface:
public interface IAsyncRunner
{
Task Run<T>(Action<T> action);
}
The class:
public class AsyncRunner : IAsyncRunner
{
private ILifetimeScope _lifetimeScope { get; set; }
public AsyncRunner(ILifetimeScope lifetimeScope)
{
//Guard.NotNull(() => lifetimeScope, lifetimeScope);
_lifetimeScope = lifetimeScope;
}
public Task Run<T>(Action<T> action)
{
Task.Factory.StartNew(() =>
{
using (var lifetimeScope = _lifetimeScope.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag))
{
var service = lifetimeScope.Resolve<T>();
action(service);
}
});
return Task.FromResult(0);
}
}
I have different classes as descendants of a base class (Worker).
Each of these classes has it's own quartz job and trigger and a
callback.
public class Worker1 : Worker, ICallback
{
IScheduler scheduler;
public Worker1(IScheduler scheduler)
: base("Worker1")
{
this.scheduler = scheduler;
IJobDetail job = JobBuilder.Create<MonitorJob>()
.WithIdentity(name + "Job")
.Build();
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity(name + "Trigger")
.StartNow()
.WithSimpleSchedule(x => x
.WithIntervalInSeconds(1)
.RepeatForever())
.Build();
scheduler.ScheduleJob(job, trigger);
}
public void Callback()
{
Console.WriteLine(name + " callback " + DateTime.Now.ToLongTimeString());
}
}
Now I want on trigger of job1 (from worker1) a callback to worker1.callback.
Same with job2 (from worker2) a callback to worker2.callback.
With autofac I am able to inject a callback into my job - however I can only inject a common callback, not as I like one for each class.
public class MonitorJob : IJob
{
ICallback callback;
public MonitorJob(ICallback callback)
{
this.callback = callback;
}
public void Execute(IJobExecutionContext context)
{
callback.Callback();
}
}
my main class creates the autofac container
container = ConfigureContainer(new ContainerBuilder()).Build();
using (container.BeginLifetimeScope())
{
workers.Add(container.Resolve<Worker1>());
workers.Add(container.Resolve<Worker2>());
}
var factory = container.Resolve<ISchedulerFactory>();
var scheduler = factory.GetScheduler();
scheduler.Start();
internal static ContainerBuilder ConfigureContainer(ContainerBuilder cb)
{
cb.RegisterModule(new QuartzAutofacFactoryModule());
cb.RegisterModule(new QuartzAutofacJobsModule(typeof(QuartzScheduler.MonitorJob).Assembly));
cb.RegisterType<Worker1>().AsSelf();
cb.RegisterType<Worker2>().AsSelf();
return cb;
}
Autofac creates the job and also could inject it with a callback, however I want the right callback from the worker class that "owns" the job.
How would this be possible?
Otherwise I would need to somehow propagate from one common callback to the respective worker class.
Thank you in advance
I succeeded by creating a generic job:
public class MonitorJob<T> : IJob where T:IWorker
{
T worker;
public MonitorJob(T worker)
{
this.worker = worker;
}
public void Execute(IJobExecutionContext context)
{
worker.Callback();
}
}
In my custom worker class I create the monitor job for this worker:
public Worker1(IScheduler scheduler)
{
this.scheduler = scheduler;
IJobDetail job = JobBuilder.Create<MonitorJob<Worker1>>()
.WithIdentity(name + "Job")
.Build();
[...]
}
and so the right callback class will be resolved by:
container.RegisterType<Worker1>().AsSelf().SingleInstance();
container.RegisterType<MonitorJob<Worker1>>().AsSelf();
[...]
workers.Add(scope.Resolve<Worker1>());
so always the right callback is executed for each individual job.
Autofac has no way to know how to bind ICallback and IJob. How would like to bind them ?
The simplest solution is to introduce a dependency between these 2 classes
public class MonitorJob : IJob
{
public MonitorJob(Worker1 worker1)
{
this._worker1 = worker1;
}
private readonly Worker1 _worker1;
public void Execute(IJobExecutionContext context)
{
this._worker1.Callback();
}
}
If it is not possible you can use Keyed service :
public class MonitorJob : IJob
{
public MonitorJob([WithKey("Worker1")]ICallback callback)
{
this._callback = callback;
}
private readonly ICallback _callback;
public void Execute(IJobExecutionContext context)
{
this._callback.Callback();
}
}
and you will have to register it this way cb.RegisterType<Worker1>().Keyed<ICallback>("Worker1")
If it is still not possible to modify your code this way, you can create a new module to add parameters :
public class CallbackModule : Module
{
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
{
if (registration.Services.OfType<TypedService>()
.Any(s => s.ServiceType == typeof(IJob)))
{
registration.Preparing += (sender, e) =>
{
// bind ICallback and IJob here
if (registration.Activator.LimitType == typeof(Job1))
{
e.Parameters = e.Parameters
.Concat(new TypedParameter[] { TypedParameter.From<ICallback>(e.Context.Resolve<Worker1>()) });
}
};
}
}
}
And the registration can be made this way :
ContainerBuilder cb = new ContainerBuilder();
cb.RegisterModule(new CallbackModule());
cb.RegisterType<Worker1>().As<ICallback>().AsSelf();
cb.RegisterType<Worker2>().As<ICallback>().AsSelf();
IContainer container = cb.Build();
By the way, the following code contains a mistake:
using (container.BeginLifetimeScope())
{
workers.Add(container.Resolve<Worker1>());
workers.Add(container.Resolve<Worker2>());
}
You create a new ILifetimeScope but don't use it. The following code should be use instead :
using (ILifetimeScope scope = container.BeginLifetimeScope())
{
workers.Add(scope.Resolve<Worker1>());
workers.Add(scope.Resolve<Worker2>());
}
If you want to be notified when the execute method is called, you can implement a JobListener for this. You can create one listener for all jobs or multiple listeners, one per job type perhaps, and then apply the appropriate matcher. Here's a link that might be useful with more details on job listeners and listeners in general: Quartz.Net Job Listeners, Part 3 of Quartz.net Listeners in detail
I am attempting a very simple application with NServiceBus and Ninject.
I am attempting to use Ninject as the container for NServiceBus, but I am getting the following error - "NServiceBus.IStartableBus is not registered in the container."
I'm sure the answer is quite obvious... just not to me!
My code is as follows
public class StartApp : IWantCustomInitialization, IWantToRunWhenBusStartsAndStops
{
private static IKernel _kernel;
public IBus Bus { get; set; }
public void Init()
{
Configure.Serialization.Json();
}
public void Start()
{
_kernel = new StandardKernel();
Configure.With()
.NinjectBuilder(_kernel)
.CreateBus()
.Start();
Bus.Send(new TestMessage {Id = Guid.NewGuid(), MessageText = "Bloop"});
}
public void Stop()
{
}
}
namespace NServiceBus_Ninject_Simple
{
public class EndpointConfig : IConfigureThisEndpoint, AsA_Server
{ }
}
This google groups discussion is about the same issue.
It seems you are creating the configuration in the wrong place.
It should look like this:
public abstract class DefaultEndpointConfig
: IConfigureThisEndpoint
, IWantCustomInitialization
{
IWantCustomInitialization.Init()
{
Configure
.With()
.NinjectBuilder();
// + any other config;
// Call optional endpoint specific config
Init();
}
public virtual void Init()
{
}
}
See here (Johannes Gustafsson)
It needs to be done in the EndPoint-Configuration (for every endpoint, this is why he suggests using a base class) and it needs to implement IConfigureThisEndpoint.