Quartz.net和Ninject:如何使用NInject将实现绑定到我的作业

时间:2014-09-17 11:24:51

标签: asp.net asp.net-mvc ninject quartz.net

我实际上在ASP.Net MVC 4 Web应用程序中工作,我们使用NInject进行依赖注入。我们还使用基于实体框架的UnitOfWork和Repositories。

我们希望在我们的应用程序中使用Quartz.net定期启动一些自定义作业。我希望NInject自动绑定我们工作中需要的服务。

可能是这样的:

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));
    }
}

到目前为止,我们的NInjectWebCommon绑定配置如下(使用请求范围):

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

是否可以使用NInject将正确的实现注入我们的自定义作业?怎么做?我已经阅读了很少关于堆栈溢出的帖子,但是我需要一些建议和一些使用NInject的例子。

4 个答案:

答案 0 :(得分:6)

在Quartz计划中使用JobFactory,并在那里解析您的作业实例。

所以,在你的NInject配置中设置工作(我在这里猜测正确的NInject语法)

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

然后,创建一个JobFactory:[编辑:这是@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);
    }
}

然后,在创建调度程序时,将JobFactory分配给它:

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

    schedule.JobFactory = new NInjectJobFactory(root);

    return schedule;
}

Quartz将使用JobFactory创建作业,NInject将为您解析依赖项。

答案 1 :(得分:4)

关于IUnitOfWork的范围,根据answer i linked的评论,你可以做

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

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

您应该注意的唯一警告: 如果在Web请求中使用了异步,则可能会错误地解析IUnitOfWork为空的工作线程中的HttpContext.Current。现在没有后备绑定,这将失败,但有一个例外,它会告诉你你做错了什么。然而,使用后备绑定,问题可能以模糊的方式出现。也就是说,它有时可能有效,但有时不行。这是因为同一请求会有两个(甚至更多)IUnitOfWork个实例。

为了解决这个问题,我们可以使绑定更具体。为此,我们需要一些参数来告诉我们使用InRequestScope()以外的其他参数。看看:

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; }
    }
}

现在调整工作工厂如下:

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);
    }
}

并调整IUnitOfWork绑定:

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

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

这样,如果您使用async错误,那么仍然会有例外,但IUnitOfWork范围仍然适用于石英任务。

答案 2 :(得分:1)

对于任何可能感兴趣的用户,这是最终为我工作的解决方案。

我已经让它做了一些调整以匹配我的项目。请注意,在NewJob方法中,我已经用_resolutionRoot.Get替换了对Kernel.Get的调用。

你可以在这里找到:

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)
    {
    }
}

以下是我的工作电话时间表:

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

非常感谢您的帮助

答案 3 :(得分:0)

非常感谢您的回复。我已经实现了类似的东西,绑定正在起作用:):

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

正如我之前所说,我在我的项目中使用了一个服务和工作单元(基于EF),它们都注入了NInject。

public class DispatchingManagementService : IDispatchingManagementService
{
    private readonly IUnitOfWork _unitOfWork;

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

请在这里找到我如何绑定实现:

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

要恢复,IUnitOfWork的绑定是为:   - 每次有新请求到我的应用程序ASP.Net MVC:请求范围   - 每次我运行这项工作时:InCallScope

根据EF的行为,最佳做法是什么?我找到了使用CallInScope的信息。每当有新的请求进入应用程序时,是否可以告诉NInject获取范围ByRequest,每次我的工作运行时,是否可以使用InCallScope?怎么做?

非常感谢您的帮助