我正在使用NServiceBus Scheduler,这就是为什么我不得不使用BeginLifetimeScope来避免内存泄漏。
方法:
public void Start()
{
using (var scope = _lifetimeScope.BeginLifetimeScope())
{
var jobs = scope.Resolve<IEnumerable<IJob>>();
foreach (var job in jobs)
{
_schedule.Every(job.GetTimeInterval(), () =>
{
job.Run();
});
}
}
}
上下文的Autofac配置:
private static void RegisterContext(ContainerBuilder builder)
{
builder.Register(c => new MyContext(GetConnectionString())).InstancePerLifetimeScope();
}
如果MyContext
有InstancePerLifetimeScope
,那么当我尝试使用它时 - 我有一个错误:System.InvalidOperationException: 'The operation cannot be completed because the DbContext has been disposed.'
因此,如果我将其更改为SingleInstance()
,那么当然一切正常但上下文没有处理,我可能会有内存泄漏......
@EDIT
我已经修复了问题,这是解决方案:
public void Start()
{
List<Type> jobTypes = new List<Type> { typeof(ExpiryDateTask) };
foreach (var jobType in jobTypes)
{
_schedule.Every(TimeSpan.FromSeconds(30), () =>
{
using (var scope = _lifetimeScope.BeginLifetimeScope())
{
var job = scope.Resolve<IJob>();
job.Run();
}
});
}
}
但我想知道如何重构这一部分:
List<Type> jobTypes = new List<Type> { typeof(ExpiryDateTask) };
-
应该通过实现的所有类型的任务以某种方式填充该列表
IJob界面。var job = scope.Resolve<IJob>();
我认为这是错误的,应该看起来更像var job = resolveJob(jobType)
- 所以基本上基于类型。 答案 0 :(得分:2)
最有可能的原因是作业是异步运行的,即在执行退出using语句后调用job.Run()
。在调用job.Run()
时,已经处理了生命周期范围,包括底层数据库上下文。
尝试在调度程序回调中移动using语句。毕竟,在单独的生命周期环境中执行每个预定作业最有意义。
我想MyContext
被注入到实现IJob
的类的构造函数中,对吧?关于你的原始代码,我认为有两个相互矛盾的目标:
GetTimeInterval
并安排不可能同时获得两者。你需要分开 来自执行部分的调度部分。
一种方法是使用Autofac的Metadata
。它允许您在实际实例化之前将有关组件的上下文信息考虑在内。我写了一篇有效的code sample来说明如何做到这一点。
归结为:
使用元数据注册作业
builder.RegisterType<JobImplementation>()
.WithMetadata<JobMetadata>(conf =>
conf.For(meta => meta.Interval, TimeSpan.FromSeconds(30))
.For(meta => meta.Type, typeof(JobImplementation))
)
.As<IJob>()
.InstancePerLifetimeScope();
使用此元数据解析调度程序回调中的特定作业实例
using (var scope = container.BeginLifetimeScope())
{
//Select this one specific job by its metadata.
var specificJobInitializer = scope
.Resolve<IEnumerable<Meta<Func<IJob>, JobMetadata>>>()
.Single(jobInitializer => jobInitializer.Metadata.Type == jobDefinition.Type);
IJob job = specificJobInitializer.Value();
job.Run();
}
答案 1 :(得分:0)
我将继续关注pbalaga。
为避免处置DbContext
的问题,一种选择是为DbContext
使用某种工厂并在作业内创建DbContext
。
另一种选择是使用提供延迟作业解析的不同调度程序,我们使用Quartz.NET与MassTransit和Topshelf,并且可以选择提供这样的调度程序:
ScheduleJobServiceConfiguratorExtensions.SchedulerFactory = () => container.Resolve<IScheduler>();
如果我没记错的话,工作在应该被解雇的那一刻得到了解决:
serviceConfigurator.ScheduleQuartzJob(q =>
{
q.WithJob(JobBuilder.Create<EventsJob>()
.WithIdentity("Events processing", "Events")
.Build);
q.AddTrigger(() => TriggerBuilder.Create().StartAt(DateTimeOffset.Now.AddSeconds(30))
.WithSchedule(SimpleScheduleBuilder.RepeatMinutelyForever(1)).Build());
});