Ik ben bezig met een hobby projectje met quartz.net en ik gebruik hierbij Autofac als IoC framework. Autofac heeft een mooie feature lifetimescope waarmee je controle kan krijgen over levensduur van objecten waarmee je bijvoorbeeld makkelijk een unit-of-work kan afbakenen. Echter loop ik tegen een probleem aan als ik die mogelijkheid wil integreren in quartz.net
Zonder controle over de levensduur is de oplossing makkelijk, de jobfactory van quartz resolved hier via autofac het type van de job:
(Misschien handig voor een mogelijke oplossing later is te weten dat als de job klaar is, hij via ReturnJob(IJob job) terugkomt).
Echter ontstaat een probleem als ik de job in een nieuwe lifetimescope wil afhandelen:
Ik krijg nu wel een nieuwe lifetimescope, maar de referentie er naar toe ben ik kwijt zodra de job wordt gereturned. Een autofac lifetimescope moet namelijk altijd disposed worden (het staat meestal in een using block).
Op stackoverflow stond opzich een slimme oplossing, zie:
Autofac and Quartz.Net Integration
Er wordt daar een wrapper om de quartz.net IJob heengezet, met een Autofac Owned<T> construct, die afdwingt dat de levensduur van objecten zich beperkt tot de executie van de quartz job.
Echter werkt die oplossing alleen als je van te voren al weet welke type quartz job er wordt opgevraagd, aangezien de jobwrapper dat afdwingt:
De functie NewJob in de jobfactory van quartz geeft echter alleen het Type terug..
Een andere oplossing is om de in de Job het volgende te doen:
Maar dat vind ik toch geen mooie oplossing, aangezien je dit toch het liefst in de jobfactory wilt afhandelen.
Een andere oplossing is de scope zelf ergens bewaren in een dictionary oid of toe te voegen aan de jobcontext, maar ook dat lijkt me meer een hack.
Samengevat: Ik wil een lifetimescope van autofac kunnen beëindigen terwijl ik de referentie naar die scope kwijt raak, is hier voor een elegante oplossing die ik misschien mis ?
Zonder controle over de levensduur is de oplossing makkelijk, de jobfactory van quartz resolved hier via autofac het type van de job:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| public class AutofacJobFactory : IJobFactory { private readonly ILifetimeScope _container; public AutofacJobFactory(ILifetimeScope container) { _container = container; } public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { Type jobtype = bundle.JobDetail.JobType; var job = (IJob)_container.Resolve(jobtype); return job; } public void ReturnJob(IJob job) { } } |
(Misschien handig voor een mogelijke oplossing later is te weten dat als de job klaar is, hij via ReturnJob(IJob job) terugkomt).
Echter ontstaat een probleem als ik de job in een nieuwe lifetimescope wil afhandelen:
C#:
1
2
3
4
5
6
7
| public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { Type jobtype = bundle.JobDetail.JobType; ILifetimeScope newJobLifetimeSope = _container.BeginLifetimeScope(); var job = (IJob)newJobLifetimeSope.Resolve(jobtype); return job; } |
Ik krijg nu wel een nieuwe lifetimescope, maar de referentie er naar toe ben ik kwijt zodra de job wordt gereturned. Een autofac lifetimescope moet namelijk altijd disposed worden (het staat meestal in een using block).
Op stackoverflow stond opzich een slimme oplossing, zie:
Autofac and Quartz.Net Integration
Er wordt daar een wrapper om de quartz.net IJob heengezet, met een Autofac Owned<T> construct, die afdwingt dat de levensduur van objecten zich beperkt tot de executie van de quartz job.
Echter werkt die oplossing alleen als je van te voren al weet welke type quartz job er wordt opgevraagd, aangezien de jobwrapper dat afdwingt:
C#:
1
| public class JobWrapper<T>: IJob where T:IJob |
De functie NewJob in de jobfactory van quartz geeft echter alleen het Type terug..
Een andere oplossing is om de in de Job het volgende te doen:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| public class SampleJob : IJob { private readonly ILifetimeScope _container; public SampleJob(ILifetimeScope container) { _container = container; } public void Execute(IJobExecutionContext context) { using (var scope = _container.BeginLifetimeScope()) { var implementation = scope.Resolve<MySampleJobImplementation>(); implementation.Execute(); } } } |
Maar dat vind ik toch geen mooie oplossing, aangezien je dit toch het liefst in de jobfactory wilt afhandelen.
Een andere oplossing is de scope zelf ergens bewaren in een dictionary oid of toe te voegen aan de jobcontext, maar ook dat lijkt me meer een hack.
Samengevat: Ik wil een lifetimescope van autofac kunnen beëindigen terwijl ik de referentie naar die scope kwijt raak, is hier voor een elegante oplossing die ik misschien mis ?
"When I am weaker than you I ask you for freedom because that is according to your principles; when I am stronger than you I take away your freedom because that is according to my principles"- Frank Herbert