我实际上在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的例子。
答案 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?怎么做?
非常感谢您的帮助