我的软件使用EFCore和ASP.NETCoreWebAPI中的SQLite数据库,使用依赖注入,有一个内存泄漏。
我有一个后台工作使用石英得到调用每9秒。
我的上下文如下所示:
public class TeslaSolarChargerContext : DbContext, ITeslaSolarChargerContext
{
public DbSet<ChargePrice> ChargePrices { get; set; } = null!;
public DbSet<HandledCharge> HandledCharges { get; set; } = null!;
public DbSet<PowerDistribution> PowerDistributions { get; set; } = null!;
public string DbPath { get; }
public void RejectChanges()
{
foreach (var entry in ChangeTracker.Entries())
{
switch (entry.State)
{
case EntityState.Modified:
case EntityState.Deleted:
entry.State = EntityState.Modified; //Revert changes made to deleted entity.
entry.State = EntityState.Unchanged;
break;
case EntityState.Added:
entry.State = EntityState.Detached;
break;
}
}
}
public TeslaSolarChargerContext()
{
}
public TeslaSolarChargerContext(DbContextOptions<TeslaSolarChargerContext> options)
: base(options)
{
}
}
与接口
public interface ITeslaSolarChargerContext
{
DbSet<ChargePrice> ChargePrices { get; set; }
DbSet<HandledCharge> HandledCharges { get; set; }
DbSet<PowerDistribution> PowerDistributions { get; set; }
ChangeTracker ChangeTracker { get; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken());
DatabaseFacade Database { get; }
void RejectChanges();
}
在我的Program.cs
中,我将上下文和Quartz作业添加到依赖注入中,如下所示:
builder.Services.AddDbContext<ITeslaSolarChargerContext, TeslaSolarChargerContext>((provider, options) =>
{
options.UseSqlite(provider.GetRequiredService<IDbConnectionStringHelper>().GetTeslaSolarChargerDbPath());
options.EnableSensitiveDataLogging();
options.EnableDetailedErrors();
}, ServiceLifetime.Transient, ServiceLifetime.Transient)
.AddTransient<IChargingCostService, ChargingCostService>();
builder.Services
.AddSingleton<JobManager>()
.AddTransient<PowerDistributionAddJob>()
.AddTransient<IJobFactory, JobFactory>()
.AddTransient<ISchedulerFactory, StdSchedulerFactory>();
我使用自己的JobManager
,因为作业间隔可以通过各种方式进行配置,所以我将一个 Package 器注入到我的JobManager
中,它是Singleton,因为我需要随时停止作业,因为作业间隔可以在运行时更新,所以我需要停止和启动作业:
public class JobManager
{
private readonly ILogger<JobManager> _logger;
private readonly IJobFactory _jobFactory;
private readonly ISchedulerFactory _schedulerFactory;
private readonly IConfigurationWrapper _configurationWrapper;
private IScheduler _scheduler;
public JobManager(ILogger<JobManager> logger, IJobFactory jobFactory, ISchedulerFactory schedulerFactory, IConfigurationWrapper configurationWrapper)
{
_logger = logger;
_jobFactory = jobFactory;
_schedulerFactory = schedulerFactory;
_configurationWrapper = configurationWrapper;
}
public async Task StartJobs()
{
_logger.LogTrace("{Method}()", nameof(StartJobs));
_scheduler = _schedulerFactory.GetScheduler().GetAwaiter().GetResult();
_scheduler.JobFactory = _jobFactory;
var powerDistributionAddJob = JobBuilder.Create<PowerDistributionAddJob>().Build();
var powerDistributionAddTrigger = TriggerBuilder.Create()
.WithSchedule(SimpleScheduleBuilder.RepeatSecondlyForever((int)_configurationWrapper.JobIntervall().TotalSeconds)).Build();
var triggersAndJobs = new Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>>
{
{powerDistributionAddJob, new HashSet<ITrigger> {powerDistributionAddTrigger}},
};
await _scheduler.ScheduleJobs(triggersAndJobs, false).ConfigureAwait(false);
await _scheduler.Start().ConfigureAwait(false);
}
public async Task StopJobs()
{
await _scheduler.Shutdown(true).ConfigureAwait(false);
}
}
作业如下所示:
[DisallowConcurrentExecution]
public class PowerDistributionAddJob : IJob
{
private readonly ILogger<ChargeTimeUpdateJob> _logger;
private readonly IChargingCostService _service;
public PowerDistributionAddJob(ILogger<ChargeTimeUpdateJob> logger, IChargingCostService service)
{
_logger = logger;
_service = service;
}
public async Task Execute(IJobExecutionContext context)
{
_logger.LogTrace("{method}({context})", nameof(Execute), context);
await _service.AddPowerDistributionForAllChargingCars().ConfigureAwait(false);
}
}
将上下文注入到服务中,如下所示:
public ChargingCostService(ILogger<ChargingCostService> logger,
ITeslaSolarChargerContext teslaSolarChargerContext)
{
_logger = logger;
_teslaSolarChargerContext = teslaSolarChargerContext;
}
我在服务中使用上下文并调用此方法:
var chargePrice = await _teslaSolarChargerContext.ChargePrices
.FirstOrDefaultAsync().ConfigureAwait(false);
调用此命令会导致应用程序在一周后使用1GB RAM。
在分析内存转储后,我发现大约8小时后,我有超过2000个TeslaSolarChargerContext
示例。
1条答案
按热度按时间2skhul331#
您是否运行了任何内存探查器来确认泄漏的位置?
我创造了一个小的最低限度的复制:
首先,它完全起作用,这意味着Quartz正在为每个作业创建作用域- DbContext在每次运行作业时创建,并被正确处理。dotMemory证实了这一点。
quartzOptions.UseMicrosoftDependencyInjectionJobFactory();
是关键:https://www.quartz-scheduler.net/documentation/quartz-3.x/packages/microsoft-di-integration.html#installation我注意到您将上下文注册为transient,可能是因为您发现无法将作用域依赖项注入到transient服务中。在
DbContext.Dispose
方法中放置一个断点,看看它是否被命中。如果没有,您在注册或保持示例时搞砸了一些事情-这就是您的漏洞。容器应该处理你的暂时依赖,但是我真的不知道你是如何注册Quartz的--也许你在那里的某个地方配置错了。看看我的复制品,看看有什么不同。
编辑:
如果您确定问题是由DbContext引起的,则可以随时切换到注入
DbContextFactory
(docs):