Quartz.net 的 Unity LifeTime 管理器
Unity LifeTime manager for Quartz.net
我正在尝试在 asp.NET MVC 应用程序中使用 Quartz.Net。我使用 Unity 作为 DI,PerRequestLifeTimeManager
。
然而,Quartz.Net 不适用于 PerRequestLifeTimeManager
,因为它没有开始的请求。我尝试用它解决的任何依赖关系 returns null.
我创建了一个类似于适配器的 class 来根据上下文使用两个生命周期管理器,如下所示:
class CustomLifetimeManager : LifetimeManager
{
private readonly string _key = "CustomLifetimeManagerKey" + Guid.NewGuid();
private readonly PerResolveLifetimeManager _perResolveLifetimeManager = new PerResolveLifetimeManager();
private bool IsWebContext => HttpContext.Current != null;
public override object GetValue()
{
return IsWebContext
? HttpContext.Current.Items[_key]
: _perResolveLifetimeManager.GetValue();
}
public override void SetValue(object newValue)
{
if (IsWebContext)
HttpContext.Current.Items[_key] = newValue;
else
_perResolveLifetimeManager.SetValue(newValue);
}
public override void RemoveValue()
{
if (IsWebContext)
HttpContext.Current.Items[_key] = null;
else
_perResolveLifetimeManager.RemoveValue();
}
}
我试过 PerThreadLifetimeManager
,它第一次执行正常,然后后续执行失败并显示消息
The operation cannot be completed because the DbContext has been
disposed.
我试过更改为 PerResolveLifeTimeManager
,但失败了
An entity object cannot be referenced by multiple instances of
IEntityChangeTracker
我的工作非常简单,类似于以下内容:
[DisallowConcurrentExecution]
class MyJob
{
IFooRepository _fooRepository;
IBarRepository _barRepository;
public MyJob(IFooRepository fooRepository, IBarRepository barRepository)
{
_fooRepository = fooRepository;
_barRepository = barRepository;
}
public void Execute(IJobExecutionContext context)
{
var foos = _fooRepository.Where(x => !x.Processed);
foreach(var foo in foos)
{
var bar = _barRepository.Where(x => x.Baz == foo.Baz);
foo.DoMagic(bar);
foo.Processed = true;
_fooRepository.Save(foo);
}
}
}
而我的工作工厂是
public class UnityJobFactory : IJobFactory
{
private readonly IUnityContainer _container;
public UnityJobFactory(IUnityContainer container)
{
_container = container;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
return (IJob)_container.Resolve(bundle.JobDetail.JobType);
}
public void ReturnJob(IJob job)
{
}
}
如何正确管理 Quartz 作业中的依赖生命周期?
我对 Castle.Windsor 和 Quartz.Net 也有同样的问题。我发现唯一适用的方法是 ScopedLifetime,但你必须自己控制范围。如果有新的请求进来,打开一个范围,所有服务都将在这个范围内解析(所谓的 UnitOfWork ;)),当请求结束时,关闭它。
作业处理有点棘手。但是你有两种方法可以解决这个问题。对于这两种方式,您都需要一个可以启动作用域的工厂。
你的作业在构造函数中获得一个工厂,在 Execute(IJobExecutionContext context)
中工厂启动一个范围,解析你的服务(存储库...)执行作业所做的任何事情并关闭范围。 using(Factory.BeginScope())
非常适合这个。这样做的缺点是,由于使用服务定位器模式,它被认为是不好的做法。
public class MyJob
{
private readonly Factory Factory;
public MyJob(Factory factory)
{
Factory = factory;
}
public void Execute(IJobExecutionContext context)
{
using (Factory.BeginScope())
{
var repo = Factory.Create<IFooRepository>();
// Do stuff
Factory.Release(repo);
}
}
}
你的工作得到一个工厂或可以启动范围的东西,你的服务就像这样:Func<IFooRepository> repositoryFunc
。然后在您的 Execute
方法中,启动范围(再次使用)并调用您的 repository
,它将 return 您在该范围内的真实存储库,您可以随意使用它.这应该是最好的方法。请注意,这不被视为服务定位器模式,因为您为您的工作提供了服务的 Func<>
并且您只是控制范围。
public class MyJob
{
private readonly Factory Factory;
private readonly Func<IFooRepository> RepositoryFunc;
public MyJob(Factory factory, Func<IFooRepository> repositoryFunc)
{
Factory = factory;
RepositoryFunc= repositoryFunc;
}
public void Execute(IJobExecutionContext context)
{
using (Factory.BeginScope())
{
var repo = RepositoryFunc();
// Do Stuff
}
}
}
问题
PerThreadLifetimeManager
The operation cannot be completed because the DbContext has been disposed.
这是因为 Quartz 使用 MainThread 并且默认情况下使用具有 10 个线程的 ThreadPool。所有作业都在 MainThread 中创建,然后在池中的空闲线程中执行。如果您启动一个作业,DBContext 将绑定到 MainThread。当你开始另一个工作时,那么已经有一个 DBContext 绑定到这个线程,无论它是被释放还是关闭,LifeTimeManager 都会解析这个已经使用的上下文。 如果您是第一次启动 Job,Thread 是新的并且您当前的 DBContext 绑定到此 Thread。当您启动下一个作业并在同一个线程中执行时,总会有一个 DBContext 绑定到该线程。 LifeTimeManager 解决了这个已经使用过的上下文,但你不能使用它,因为它已关闭。
PerResolveLifeTimeManager
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
这个问题来自 EF。您解析的每个服务都会获得一个新的范围,即使您使用相同的构造函数解析不同的服务也是如此。这导致您使用的每个存储库都有自己的 DBContext。并且 EF 禁止对同一实体使用不同的 DBContext。
很久以前就用 simpleinjector 让它工作了。
虽然这是旧版本的 Quartz,希望它仍然可以帮助
您需要创建自定义 LifetimeScope
public class LifetimeScopeJobDecorator : IJob
{
private readonly IJob _decoratee;
private readonly Container _container;
public LifetimeScopeJobDecorator(IJob decoratee, Container container)
{
_decoratee = decoratee;
_container = container;
}
public void Execute(IJobExecutionContext context)
{
using (_container.BeginLifetimeScope())
{
_decoratee.Execute(context);
}
}
}
你打电话到你的工作工厂
public class SimpleInjectorJobFactory : IJobFactory
{
private readonly Container _container;
public SimpleInjectorJobFactory(Container container)
{
_container = container;
_container.Verify();
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
IJobDetail jobDetail = bundle.JobDetail;
Type jobType = jobDetail.JobType;
var job = (IJob)_container.GetInstance(jobType);
return new LifetimeScopeJobDecorator(job, _container);
}
public void ReturnJob(IJob job)
{
}
}
然后您可以初始化您的自定义 Quartz 容器
public static class QuartzScheduler
{
private static Container _quartzContainer { get; set; }
private static void Initialize()
{
Container container = new Container();
container.RegisterLifetimeScope<IUnitOfWork, SqlUnitOfWork>();
container.Register<ILogger, NLogLogger>();
//To enable lifetime scoping, please make sure the EnableLifetimeScoping extension method is called during the configuration of the container.
container.EnableLifetimeScoping();
container.Verify();
_quartzContainer = new Container();
var schedulerFactory = new StdSchedulerFactory();
_quartzContainer.RegisterSingle<IJobFactory>(() => new SimpleInjectorJobFactory(container));
_quartzContainer.RegisterSingle<ISchedulerFactory>(schedulerFactory);
_quartzContainer.Register<IScheduler>(() =>
{
var scheduler = schedulerFactory.GetScheduler();
scheduler.JobFactory = _quartzContainer.GetInstance<IJobFactory>();
return scheduler;
}
);
_quartzContainer.Verify();
启动调度程序
public static void StartJobs()
{
Initialize();
//Ask the scheduler factory for a scheduler
IScheduler scheduler = _quartzContainer.GetInstance<IScheduler>();
scheduler.Start();
}
请看一下 Quartz.Unity nuget 包 https://github.com/hbiarge/Quartz.Unity,这个 unity 包有一个很好的 ScopedLifetime 实现。
除了上述 nuget 包之外,如果您使用多个统一容器实例并将 lifetimemanager 作为委托传递,它将允许您正确处理一次性类型,例如每个 quartz 作业的 DBContext 以及每个http 请求。
您必须为 asp.net mvc / web api 设置一个单独的 IUnityContainer 实例,并为 Quartz 调度程序设置另一个 IUnityContainer 实例。
这是一个完整的工作示例
https://github.com/vinodres/DITestingWithQuartz
如果你看QuartzStartup.cs,我已经用它来初始化 Quartz Scheduler。为简单起见,假设 IHelloService 是一种一次性类型,它必须在每个作业结束时以及每个 http 请求结束时被释放。在这里,我创建了一个单独的实例
IUnityContainer 分配给 QuartzContainer 并添加了来自 Quartz.Unity nuget 包的名为 QuartzUnityExtention 的新扩展。还调用了我在另一个名为 unityconfig.cs 的文件中创建的 .Configure 扩展方法。该方法以 Func 作为参数。该参数允许您根据执行路径传递不同的生命周期管理器实例。
这里是QuartzStartup.cs
[assembly: OwinStartup(typeof(DiTestingApp.QuartzStartup))]
namespace DiTestingApp
{
/// <summary>
///
/// </summary>
public class QuartzStartup
{
private static readonly ILog Log = LogManager.GetLogger(typeof(QuartzStartup));
/// <summary>
/// Get the hangfire container.
/// </summary>
private static readonly Lazy<IUnityContainer> QuartzContainer = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
container.AddNewExtension<QuartzUnityExtension>();
container.Configure(() => new HierarchicalLifetimeManager());
return container;
});
/// <summary>
///
/// </summary>
/// <param name="app"></param>
public void Configuration(IAppBuilder app)
{
Log.Info("Quartz Startup Intitializing...");
var container = QuartzContainer.Value;
InitScheduler(container);
Log.Info("Quartz Startup Intialization Complete...");
var properties = new AppProperties(app.Properties);
var cancellationToken = properties.OnAppDisposing;
if (cancellationToken != CancellationToken.None)
{
cancellationToken.Register(() =>
{
QuartzContainer.Value.Dispose();
Log.Info("Quartz container disposed (app pool shutdown).");
});
}
}
private void InitScheduler(IUnityContainer container)
{
try
{
var scheduler = container.Resolve<IScheduler>();
scheduler.Start();
IJobDetail job = JobBuilder.Create<HelloWorldJob>().Build();
ITrigger trigger = TriggerBuilder.Create()
.WithSimpleSchedule(x => x.WithIntervalInSeconds(20).RepeatForever())
.Build();
scheduler.ScheduleJob(job, trigger);
}
catch (Exception ex)
{
Log.Error(ex);
}
}
}
}
我对 asp.net mvc / web api 依赖解析器配置进行了类似的设置。我创建了一个名为 UnityMvcActivator.cs 的文件,在这里当我调用 .Configure 扩展方法时,我正在传递 PerRequestLifetimeManager.
UnityMvcActivator.cs
using System;
using System.Linq;
using System.Web.Http;
using System.Web.Mvc;
using Common.Logging;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Mvc;
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(DiTestingApp.App_Start.UnityWebActivator), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(DiTestingApp.App_Start.UnityWebActivator), "Shutdown")]
namespace DiTestingApp.App_Start
{
/// <summary>Provides the bootstrapping for integrating Unity with ASP.NET MVC.</summary>
public static class UnityWebActivator
{
private static readonly ILog Log = LogManager.GetLogger(typeof(UnityWebActivator));
/// <summary>
/// Get the hangfire container.
/// </summary>
private static readonly Lazy<IUnityContainer> WebContainer = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
container.Configure(() => new PerRequestLifetimeManager());
return container;
});
/// <summary>Integrates Unity when the application starts.</summary>
public static void Start()
{
Log.Info("Web api DI container intializing.");
var container = WebContainer.Value;
FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().First());
FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(container));
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
// TODO: Uncomment if you want to use PerRequestLifetimeManager
Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
var resolver = new Microsoft.Practices.Unity.WebApi.UnityDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = resolver;
Log.Info("Web api DI container intialization complete.");
}
/// <summary>Disposes the Unity container when the application is shut down.</summary>
public static void Shutdown()
{
Log.Info("Web api DI container disposing.");
var container = WebContainer.Value;
container.Dispose();
}
}
}
现在是向 IUnityContainer 注册类型的部分。这是 UnityConfig.cs 中配置方法的实现。在这里,我注册了 IHelloService 以使用 disposableLifetimeManager 委托。调用委托时,会根据您的执行路径提供适当的生命周期管理器。如果在 asp.net mvc / web api 的上下文中使用 IHelloService,它将是 PerRequestLifetimeManager。如果它在 Quartz Job 中使用,它将是 HierarchicalLifetimeManager。
UnityConfig.cs
using System;
using DiTestingApp.Models;
using Microsoft.Practices.Unity;
using Quartz;
using Testing.Scheduler;
namespace DiTestingApp
{
/// <summary>
/// Specifies the Unity configuration for the main container.
/// </summary>
public static class UnityConfig
{
/// <summary>
///
/// </summary>
/// <param name="container"></param>
/// <param name="disposableLifetimeManager"></param>
/// <returns></returns>
public static IUnityContainer Configure(this IUnityContainer container, Func<LifetimeManager> disposableLifetimeManager )
{
container.RegisterType<IJob, HelloWorldJob>();
container.RegisterType<IHelloService, HelloService>(disposableLifetimeManager());
return container;
}
}
}
HierarchicalLifetimeManager 用于 Quartz 执行路径,因此任何一次性类型都将在每个作业结束时妥善处理。
如果 Quartz.Unity 实现不足以满足您的用例,您可以随时对其进行进一步自定义。
我正在尝试在 asp.NET MVC 应用程序中使用 Quartz.Net。我使用 Unity 作为 DI,PerRequestLifeTimeManager
。
Quartz.Net 不适用于 PerRequestLifeTimeManager
,因为它没有开始的请求。我尝试用它解决的任何依赖关系 returns null.
我创建了一个类似于适配器的 class 来根据上下文使用两个生命周期管理器,如下所示:
class CustomLifetimeManager : LifetimeManager
{
private readonly string _key = "CustomLifetimeManagerKey" + Guid.NewGuid();
private readonly PerResolveLifetimeManager _perResolveLifetimeManager = new PerResolveLifetimeManager();
private bool IsWebContext => HttpContext.Current != null;
public override object GetValue()
{
return IsWebContext
? HttpContext.Current.Items[_key]
: _perResolveLifetimeManager.GetValue();
}
public override void SetValue(object newValue)
{
if (IsWebContext)
HttpContext.Current.Items[_key] = newValue;
else
_perResolveLifetimeManager.SetValue(newValue);
}
public override void RemoveValue()
{
if (IsWebContext)
HttpContext.Current.Items[_key] = null;
else
_perResolveLifetimeManager.RemoveValue();
}
}
我试过 PerThreadLifetimeManager
,它第一次执行正常,然后后续执行失败并显示消息
The operation cannot be completed because the DbContext has been disposed.
我试过更改为 PerResolveLifeTimeManager
,但失败了
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
我的工作非常简单,类似于以下内容:
[DisallowConcurrentExecution]
class MyJob
{
IFooRepository _fooRepository;
IBarRepository _barRepository;
public MyJob(IFooRepository fooRepository, IBarRepository barRepository)
{
_fooRepository = fooRepository;
_barRepository = barRepository;
}
public void Execute(IJobExecutionContext context)
{
var foos = _fooRepository.Where(x => !x.Processed);
foreach(var foo in foos)
{
var bar = _barRepository.Where(x => x.Baz == foo.Baz);
foo.DoMagic(bar);
foo.Processed = true;
_fooRepository.Save(foo);
}
}
}
而我的工作工厂是
public class UnityJobFactory : IJobFactory
{
private readonly IUnityContainer _container;
public UnityJobFactory(IUnityContainer container)
{
_container = container;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
return (IJob)_container.Resolve(bundle.JobDetail.JobType);
}
public void ReturnJob(IJob job)
{
}
}
如何正确管理 Quartz 作业中的依赖生命周期?
我对 Castle.Windsor 和 Quartz.Net 也有同样的问题。我发现唯一适用的方法是 ScopedLifetime,但你必须自己控制范围。如果有新的请求进来,打开一个范围,所有服务都将在这个范围内解析(所谓的 UnitOfWork ;)),当请求结束时,关闭它。
作业处理有点棘手。但是你有两种方法可以解决这个问题。对于这两种方式,您都需要一个可以启动作用域的工厂。
你的作业在构造函数中获得一个工厂,在
Execute(IJobExecutionContext context)
中工厂启动一个范围,解析你的服务(存储库...)执行作业所做的任何事情并关闭范围。using(Factory.BeginScope())
非常适合这个。这样做的缺点是,由于使用服务定位器模式,它被认为是不好的做法。public class MyJob { private readonly Factory Factory; public MyJob(Factory factory) { Factory = factory; } public void Execute(IJobExecutionContext context) { using (Factory.BeginScope()) { var repo = Factory.Create<IFooRepository>(); // Do stuff Factory.Release(repo); } } }
你的工作得到一个工厂或可以启动范围的东西,你的服务就像这样:
Func<IFooRepository> repositoryFunc
。然后在您的Execute
方法中,启动范围(再次使用)并调用您的repository
,它将 return 您在该范围内的真实存储库,您可以随意使用它.这应该是最好的方法。请注意,这不被视为服务定位器模式,因为您为您的工作提供了服务的Func<>
并且您只是控制范围。public class MyJob { private readonly Factory Factory; private readonly Func<IFooRepository> RepositoryFunc; public MyJob(Factory factory, Func<IFooRepository> repositoryFunc) { Factory = factory; RepositoryFunc= repositoryFunc; } public void Execute(IJobExecutionContext context) { using (Factory.BeginScope()) { var repo = RepositoryFunc(); // Do Stuff } } }
问题
PerThreadLifetimeManager
The operation cannot be completed because the DbContext has been disposed.
这是因为 Quartz 使用 MainThread 并且默认情况下使用具有 10 个线程的 ThreadPool。所有作业都在 MainThread 中创建,然后在池中的空闲线程中执行。如果您启动一个作业,DBContext 将绑定到 MainThread。当你开始另一个工作时,那么已经有一个 DBContext 绑定到这个线程,无论它是被释放还是关闭,LifeTimeManager 都会解析这个已经使用的上下文。
如果您是第一次启动 Job,Thread 是新的并且您当前的 DBContext 绑定到此 Thread。当您启动下一个作业并在同一个线程中执行时,总会有一个 DBContext 绑定到该线程。 LifeTimeManager 解决了这个已经使用过的上下文,但你不能使用它,因为它已关闭。PerResolveLifeTimeManager
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
这个问题来自 EF。您解析的每个服务都会获得一个新的范围,即使您使用相同的构造函数解析不同的服务也是如此。这导致您使用的每个存储库都有自己的 DBContext。并且 EF 禁止对同一实体使用不同的 DBContext。
很久以前就用 simpleinjector 让它工作了。 虽然这是旧版本的 Quartz,希望它仍然可以帮助
您需要创建自定义 LifetimeScope
public class LifetimeScopeJobDecorator : IJob
{
private readonly IJob _decoratee;
private readonly Container _container;
public LifetimeScopeJobDecorator(IJob decoratee, Container container)
{
_decoratee = decoratee;
_container = container;
}
public void Execute(IJobExecutionContext context)
{
using (_container.BeginLifetimeScope())
{
_decoratee.Execute(context);
}
}
}
你打电话到你的工作工厂
public class SimpleInjectorJobFactory : IJobFactory
{
private readonly Container _container;
public SimpleInjectorJobFactory(Container container)
{
_container = container;
_container.Verify();
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
IJobDetail jobDetail = bundle.JobDetail;
Type jobType = jobDetail.JobType;
var job = (IJob)_container.GetInstance(jobType);
return new LifetimeScopeJobDecorator(job, _container);
}
public void ReturnJob(IJob job)
{
}
}
然后您可以初始化您的自定义 Quartz 容器
public static class QuartzScheduler
{
private static Container _quartzContainer { get; set; }
private static void Initialize()
{
Container container = new Container();
container.RegisterLifetimeScope<IUnitOfWork, SqlUnitOfWork>();
container.Register<ILogger, NLogLogger>();
//To enable lifetime scoping, please make sure the EnableLifetimeScoping extension method is called during the configuration of the container.
container.EnableLifetimeScoping();
container.Verify();
_quartzContainer = new Container();
var schedulerFactory = new StdSchedulerFactory();
_quartzContainer.RegisterSingle<IJobFactory>(() => new SimpleInjectorJobFactory(container));
_quartzContainer.RegisterSingle<ISchedulerFactory>(schedulerFactory);
_quartzContainer.Register<IScheduler>(() =>
{
var scheduler = schedulerFactory.GetScheduler();
scheduler.JobFactory = _quartzContainer.GetInstance<IJobFactory>();
return scheduler;
}
);
_quartzContainer.Verify();
启动调度程序
public static void StartJobs()
{
Initialize();
//Ask the scheduler factory for a scheduler
IScheduler scheduler = _quartzContainer.GetInstance<IScheduler>();
scheduler.Start();
}
请看一下 Quartz.Unity nuget 包 https://github.com/hbiarge/Quartz.Unity,这个 unity 包有一个很好的 ScopedLifetime 实现。
除了上述 nuget 包之外,如果您使用多个统一容器实例并将 lifetimemanager 作为委托传递,它将允许您正确处理一次性类型,例如每个 quartz 作业的 DBContext 以及每个http 请求。
您必须为 asp.net mvc / web api 设置一个单独的 IUnityContainer 实例,并为 Quartz 调度程序设置另一个 IUnityContainer 实例。
这是一个完整的工作示例 https://github.com/vinodres/DITestingWithQuartz
如果你看QuartzStartup.cs,我已经用它来初始化 Quartz Scheduler。为简单起见,假设 IHelloService 是一种一次性类型,它必须在每个作业结束时以及每个 http 请求结束时被释放。在这里,我创建了一个单独的实例 IUnityContainer 分配给 QuartzContainer 并添加了来自 Quartz.Unity nuget 包的名为 QuartzUnityExtention 的新扩展。还调用了我在另一个名为 unityconfig.cs 的文件中创建的 .Configure 扩展方法。该方法以 Func 作为参数。该参数允许您根据执行路径传递不同的生命周期管理器实例。
这里是QuartzStartup.cs
[assembly: OwinStartup(typeof(DiTestingApp.QuartzStartup))]
namespace DiTestingApp
{
/// <summary>
///
/// </summary>
public class QuartzStartup
{
private static readonly ILog Log = LogManager.GetLogger(typeof(QuartzStartup));
/// <summary>
/// Get the hangfire container.
/// </summary>
private static readonly Lazy<IUnityContainer> QuartzContainer = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
container.AddNewExtension<QuartzUnityExtension>();
container.Configure(() => new HierarchicalLifetimeManager());
return container;
});
/// <summary>
///
/// </summary>
/// <param name="app"></param>
public void Configuration(IAppBuilder app)
{
Log.Info("Quartz Startup Intitializing...");
var container = QuartzContainer.Value;
InitScheduler(container);
Log.Info("Quartz Startup Intialization Complete...");
var properties = new AppProperties(app.Properties);
var cancellationToken = properties.OnAppDisposing;
if (cancellationToken != CancellationToken.None)
{
cancellationToken.Register(() =>
{
QuartzContainer.Value.Dispose();
Log.Info("Quartz container disposed (app pool shutdown).");
});
}
}
private void InitScheduler(IUnityContainer container)
{
try
{
var scheduler = container.Resolve<IScheduler>();
scheduler.Start();
IJobDetail job = JobBuilder.Create<HelloWorldJob>().Build();
ITrigger trigger = TriggerBuilder.Create()
.WithSimpleSchedule(x => x.WithIntervalInSeconds(20).RepeatForever())
.Build();
scheduler.ScheduleJob(job, trigger);
}
catch (Exception ex)
{
Log.Error(ex);
}
}
}
}
我对 asp.net mvc / web api 依赖解析器配置进行了类似的设置。我创建了一个名为 UnityMvcActivator.cs 的文件,在这里当我调用 .Configure 扩展方法时,我正在传递 PerRequestLifetimeManager.
UnityMvcActivator.cs
using System;
using System.Linq;
using System.Web.Http;
using System.Web.Mvc;
using Common.Logging;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Mvc;
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(DiTestingApp.App_Start.UnityWebActivator), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(DiTestingApp.App_Start.UnityWebActivator), "Shutdown")]
namespace DiTestingApp.App_Start
{
/// <summary>Provides the bootstrapping for integrating Unity with ASP.NET MVC.</summary>
public static class UnityWebActivator
{
private static readonly ILog Log = LogManager.GetLogger(typeof(UnityWebActivator));
/// <summary>
/// Get the hangfire container.
/// </summary>
private static readonly Lazy<IUnityContainer> WebContainer = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
container.Configure(() => new PerRequestLifetimeManager());
return container;
});
/// <summary>Integrates Unity when the application starts.</summary>
public static void Start()
{
Log.Info("Web api DI container intializing.");
var container = WebContainer.Value;
FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().First());
FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(container));
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
// TODO: Uncomment if you want to use PerRequestLifetimeManager
Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
var resolver = new Microsoft.Practices.Unity.WebApi.UnityDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = resolver;
Log.Info("Web api DI container intialization complete.");
}
/// <summary>Disposes the Unity container when the application is shut down.</summary>
public static void Shutdown()
{
Log.Info("Web api DI container disposing.");
var container = WebContainer.Value;
container.Dispose();
}
}
}
现在是向 IUnityContainer 注册类型的部分。这是 UnityConfig.cs 中配置方法的实现。在这里,我注册了 IHelloService 以使用 disposableLifetimeManager 委托。调用委托时,会根据您的执行路径提供适当的生命周期管理器。如果在 asp.net mvc / web api 的上下文中使用 IHelloService,它将是 PerRequestLifetimeManager。如果它在 Quartz Job 中使用,它将是 HierarchicalLifetimeManager。
UnityConfig.cs
using System;
using DiTestingApp.Models;
using Microsoft.Practices.Unity;
using Quartz;
using Testing.Scheduler;
namespace DiTestingApp
{
/// <summary>
/// Specifies the Unity configuration for the main container.
/// </summary>
public static class UnityConfig
{
/// <summary>
///
/// </summary>
/// <param name="container"></param>
/// <param name="disposableLifetimeManager"></param>
/// <returns></returns>
public static IUnityContainer Configure(this IUnityContainer container, Func<LifetimeManager> disposableLifetimeManager )
{
container.RegisterType<IJob, HelloWorldJob>();
container.RegisterType<IHelloService, HelloService>(disposableLifetimeManager());
return container;
}
}
}
HierarchicalLifetimeManager 用于 Quartz 执行路径,因此任何一次性类型都将在每个作业结束时妥善处理。
如果 Quartz.Unity 实现不足以满足您的用例,您可以随时对其进行进一步自定义。