Quartz.net and Ninject: how to bind an implementation to my work using NInject

I am actually working in an ASP.Net MVC 4 web application where we are using NInject for dependency injection. We also use UnitOfWork and Repositories based on the Entity framework.

We would like to use Quartz.net in our application to periodically run a special task. I would like NInject to automatically bind the services that we need in our work.

It could be something like this:

public class DispatchingJob : IJob
{
    private readonly IDispatchingManagementService _dispatchingManagementService;

    public DispatchingJob(IDispatchingManagementService dispatchingManagementService )
    {
         _dispatchingManagementService = dispatchingManagementService ;
    }

    public void Execute(IJobExecutionContext context)
    {
         LogManager.Instance.Info(string.Format("Dispatching job started at: {0}", DateTime.Now));
        _dispatchingManagementService.DispatchAtomicChecks();
        LogManager.Instance.Info(string.Format("Dispatching job ended at: {0}", DateTime.Now));
    }
}

      

So far in our binding, the NInjectWebCommon is configured like this (using the request scope):

     kernel.Bind<IDispatchingManagementService>().To<DispatchingManagementService>();

      

Is it possible to inject the correct implementation into our custom job using NInject? but how to do it? I have read several posts on Stack Overflow, however I need some advice and in some cases NInject.

+3


source to share


4 answers


For users who might be interested, here is the solution that finally worked for me.

I got it working by doing some tweaking to fit my project. Note that in the NewJob method I have replaced the call with Kernel.Get _resolutionRoot.Get.

As you can find it here:

public class JobFactory : IJobFactory
{
    private readonly IResolutionRoot _resolutionRoot;

    public JobFactory(IResolutionRoot resolutionRoot)
    {
        this._resolutionRoot = resolutionRoot;
    }

    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
        try
        {
            return (IJob)_resolutionRoot.Get(
                 bundle.JobDetail.JobType, new NonRequestScopedParameter()); // parameter goes here
        }
        catch (Exception ex)
        {
            LogManager.Instance.Info(string.Format("Exception raised in JobFactory"));
        }
    }

    public void ReturnJob(IJob job)
    {
    }
}

      



And here is the call graph of my work:

    public static void RegisterScheduler(IKernel kernel)
    {
        try
        {
            var scheduler = new StdSchedulerFactory().GetScheduler();
            scheduler.JobFactory = new JobFactory(kernel);
            ....
        }
    }

      

Many thanks for your help.

+1


source


Use JobFactory in quartz schedule and resolve the job instance there.

So, your NInject config has the job set up (I assume the NInject syntax is correct here)

// Assuming you only have one IJob
kernel.Bind<IJob>().To<DispatchingJob>();

      

Then create a JobFactory: [edit: this is a modified version of @BatteryBackupUnit's answer here ]



public class NInjectJobFactory : IJobFactory
{
    private readonly IResolutionRoot resolutionRoot;

    public NinjectJobFactory(IResolutionRoot resolutionRoot)
    {
        this.resolutionRoot = resolutionRoot;
    }

    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
        // If you have multiple jobs, specify the name as
        // bundle.JobDetail.JobType.Name, or pass the type, whatever
        // NInject wants..
        return (IJob)this.resolutionRoot.Get<IJob>();
    }

    public void ReturnJob(IJob job)
    {
        this.resolutionRoot.Release(job);
    }
}

      

Then, when you create the scheduler, assign the JobFactory to it:

private IScheduler GetSchedule(IResolutionRoot root)
{
    var schedule = new StdSchedulerFactory().GetScheduler();

    schedule.JobFactory = new NInjectJobFactory(root);

    return schedule;
}

      

Quartz will then use the JobFactory to create the job and NInject will resolve the dependencies for you.

+7


source


As far as scope is concerned IUnitOfWork

, as per the comment in i linked answer , you can do

// default for web requests
Bind<IUnitOfWork>().To<UnitOfWork>()
    .InRequestScope();

// fall back to `InCallScope()` when there no web request.
Bind<IUnitOfWork>().To<UnitOfWork>()
    .When(x => HttpContext.Current == null)
    .InCallScope();

      

Just one caveat you should be aware of: If you use async incorrectly in a web request, you could mistakenly allow IUnitOfWork

in a worker thread where HttpContext.Current

is null. Now, without binding the fallback, it will fail with an exception that will show you that you did something wrong. However, when it comes to fallback binding, the problem can be latent. That is, it may work sometimes, but sometimes it doesn't. This is because there will be two (or even more) instances for the same request IUnitOfWork

.

To fix this, we can make the binding more specific. For this we need some kind of parameter to tell us to use other than InRequestScope()

. Take a look:

public class NonRequestScopedParameter : Ninject.Parameters.IParameter
{
    public bool Equals(IParameter other)
    {
        if (other == null)
        {
            return false;
        }

        return other is NonRequestScopedParameter;
    }

    public object GetValue(IContext context, ITarget target)
    {
        throw new NotSupportedException("this parameter does not provide a value");
    }

    public string Name
    {
        get { return typeof(NonRequestScopedParameter).Name; }
    }

    // this is very important
    public bool ShouldInherit
    {
        get { return true; }
    }
}

      

now adapt the factory job like this:

public class NInjectJobFactory : IJobFactory
{
    private readonly IResolutionRoot resolutionRoot;

    public NinjectJobFactory(IResolutionRoot resolutionRoot)
    {
        this.resolutionRoot = resolutionRoot;
    }

    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
         return (IJob) this.resolutionRoot.Get(
                          bundle.JobDetail.JobType,
                          new NonrequestScopedParameter()); // parameter goes here
    }

    public void ReturnJob(IJob job)
    {
        this.resolutionRoot.Release(job);
    }
}

      

and adapt the bindings IUnitOfWork

:

Bind<IUnitOfWork>().To<UnitOfWork>()
    .InRequestScope();

Bind<IUnitOfWork>().To<UnitOfWork>()
    .When(x => x.Parameters.OfType<NonRequestScopedParameter>().Any())
    .InCallScope();

      

This way, if you use the wrong one async

, there will still be an exception, but scope IUnitOfWork

will still work for quartz tasks.

+3


source


Thank you very much for your response. I've implemented something like this and the binding works :):

    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
            var resolver = DependencyResolver.Current;
            var myJob = (IJob)resolver.GetService(typeof(IJob));
            return myJob;
    }

      

As I said before, I use in my project a service and work unit (based on EF), which are both injected using NInject.

public class DispatchingManagementService : IDispatchingManagementService
{
    private readonly IUnitOfWork _unitOfWork;

    public DispatchingManagementService(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }
}

      

Please find here how I bind implementations:

kernel.Bind<IUnitOfWork>().To<EfUnitOfWork>()
kernel.Bind<IDispatchingManagementService>().To<DispatchingManagementService>();
kernel.Bind<IJob>().To<DispatchingJob>(); 

      

To resume, the IUnitOfWork binding is done for: - The time when a new ASP.Net MVC request comes to my application: request scope - Every time I execute the job: InCallScope

What are the best practices for EF behavior? I found information for using CallInScope. Is it possible to tell NInject to get a ByRequest scope every time a new request comes to the application, and an InCallScope every time my work is done? How to do it?

Many thanks for your help.

0


source







All Articles