BeginLifetimeScope / DbContext的Autofac和内存泄漏已被处理/ C#asp.net

时间:2018-05-30 18:55:28

标签: c# asp.net autofac

我正在使用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();
}

如果MyContextInstancePerLifetimeScope,那么当我尝试使用它时 - 我有一个错误: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();
            }
        });
    }
}

但我想知道如何重构这一部分:

  1. List<Type> jobTypes = new List<Type> { typeof(ExpiryDateTask) }; - 应该通过实现的所有类型的任务以某种方式填充该列表 IJob界面。
  2. var job = scope.Resolve<IJob>();我认为这是错误的,应该看起来更像var job = resolveJob(jobType) - 所以基本上基于类型。

2 个答案:

答案 0 :(得分:2)

最有可能的原因是作业是异步运行的,即在执行退出using语句后调用job.Run()。在调用job.Run()时,已经处理了生命周期范围,包括底层数据库上下文。

尝试在调度程序回调中移动using语句。毕竟,在单独的生命周期环境中执行每个预定作业最有意义。

更多详情:

我想MyContext被注入到实现IJob的类的构造函数中,对吧?关于你的原始代码,我认为有两个相互矛盾的目标:

  1. 您将每个作业预先实例化为GetTimeInterval并安排
  2. 每个作业可以多次运行,每次执行都应该拥有自己的DB上下文,并在完成后将其处理掉。
  3.   

    不可能同时获得两者。你需要分开   来自执行部分的调度部分。

    一种方法是使用Autofac的Metadata。它允许您在实际实例化之前将有关组件的上下文信息考虑在内。我写了一篇有效的code sample来说明如何做到这一点。

    归结为:

    1. 使用元数据注册作业

      builder.RegisterType<JobImplementation>()
          .WithMetadata<JobMetadata>(conf =>
              conf.For(meta => meta.Interval, TimeSpan.FromSeconds(30))
                  .For(meta => meta.Type, typeof(JobImplementation))
                  )
          .As<IJob>()
          .InstancePerLifetimeScope();
      
    2. 使用此元数据解析调度程序回调中的特定作业实例

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