In my solution, I use EF with repository pattern and Ninject inside my main web project to resolve controller dependencies, I also have a Job project that manages Quartz jobs - what is the proper way to link Ninject with Quartz so that EF will work?
I know I need to implement custom JobFactory, so I created the following in my Job project:
public class NinjectJobFactory : IJobFactory
{
private readonly IKernel resolutionRoot;
public NinjectJobFactory(IKernel resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
return (IJob)this.resolutionRoot.Get(bundle.JobDetail.JobType);
}
public void ReturnJob(IJob job)
{
string dud = "";
}
}
And in order to initialize quartz Jobs I do the following:
- Inside Global.asax: I first initialize my NinjectResolver, and then pass the NinjectKernel to JobManager class, as a parameter.
Then inside JobManager, I add jobs and runs the scheduler like this:
public void Start() { ISchedulerFactory sf = new StdSchedulerFactory(); IScheduler sched = sf.GetScheduler(); sf.GetScheduler().JobFactory = new NinjectJobFactory(ninjectKernel); DateTimeOffset runTime = DateBuilder.EvenMinuteDate(DateTime.UtcNow); DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(null, 10); IJobDetail job = JobBuilder.Create<CheckIfAllSaleActionDataIsValid>() .WithIdentity("CheckIfAllSaleActionDataIsValid", "HostessStatJobs") .Build(); job.JobDataMap.Add("SiteUrl", SiteUrl); ITrigger trigger = TriggerBuilder.Create() .WithIdentity("CheckIfAllSaleActionDataIsValidTrigger", "HostessStatJobTriggers") .StartAt(startTime) .WithCronSchedule("0 0 0/1 1/1 * ? *") .Build(); sched.ScheduleJob(job, trigger); IJobDetail job2 = JobBuilder.Create<CheckPaymentAndAssigneRoles>() .WithIdentity("CheckPaymentAndAssigneRoles", "HostessStatJobs") .Build(); job2.JobDataMap.Add("commonMethods", ninjectKernel.Get<CommonMethods>()); ITrigger trigger2 = TriggerBuilder.Create() .WithIdentity("CheckPaymentAndAssigneRolesTrigger", "HostessStatJobTriggers") .StartAt(startTime) .WithCronSchedule("0 0/1 * 1/1 * ? *") .Build(); sched.ScheduleJob(job2, trigger2); sched.Start(); }
Problem is that DbContext of each object generated by NinjectJobFactory is different, and I cannot use EF because of this (I get: An entity object cannot be referenced by multiple instances of IEntityChangeTracker which is understandable since each object in my repository has reference to different DbContext).
Weird part is that - If I manually call NinjectKernel.Get(); - for example - inside Global.asax I can clearly see that single context is passed - as it should be.
My context is binded in my Ninjectresolver like this: ninjectKernel.Bind().ToSelf().InRequestScope(); - can this be the problem?
Questions are:
- What am I doing wrong?
- If this is because I pass my NinjectKernel around - would creating different NinjectResolver inside my Job project help?
- If AD2 is true - is it advisable to create different IoC for each project, or should we do it only when really necessary ?
EDIT:
Sorry for the late response, It would appear that InRequestScope was in fact the problem - I have created separate ninject kernel inside my QuartzJobs sub-project, and:
- When I use InTransientScope - I got the same behaviour as before, that is: different DbContext for every object.
- When I use InSingletonScope or InThreadScope - it works, I got single DbContext for every object, however - DbContext does not change every time job starts.
Question is - won't it break my database? As I understand, it is recommended to use new DbContext for each user request to avoid database deadlocks - correct me if I am wrong, but it is not necessary for a Quartz job project? I mean - this project will always use single connection, so it shouldn't deadlock the database?
Am I correct? If not - what is the correct way to inject different DbContext every time Job is run? InRequestScope will not work outside MVC project...