Мы наблюдаем некоторые странные, на первый взгляд, случайные исключения, которые появляются на нашем сервере сборки во время наших тестовых прогонов do tnet.
Он всегда связан с указанным c IHostedService
, который отправляется в базу данных и очищает записи с интервалом в 60 секунд:
public class SessionCleanupHostedService : IHostedService, IDisposable
{
/*Constructor, fields and properties*/
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Session cleanup started. All expired sessions will be removed");
_timer = new Timer(CleanUp, null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
return Task.CompletedTask;
}
private void CleanUp(object state)
{
//Singletons need to create their own scope, so we can access scoped services.
using(var scope = Services?.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ApplicationContext>();
var sessionsToDelete = context.UserSession.Where(us => us.ExpiresAt < DateTime.UtcNow);
context.UserSession.RemoveRange(sessionsToDelete);
context.SaveChanges();
}
}
/*StopAsync removed */
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_timer?.Dispose();
}
}
}
Ошибка возникает в using Statement using(var scope = Services.CreateScope())
Это то, что появляется в журналах:
The active test run was aborted. Reason: Unhandled Exception: System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'IServiceProvider'.
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ThrowHelper.ThrowObjectDisposedException()
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Namespace.SessionCleanupHostedService.CleanUp(Object state) in X:\s\Folders\Auth\SessionCleanupHostedService.cs:line 35
Я извлек ветку, которая вызывает эти исключения при сборке, и попытался воспроизвести это локально, но исключение просто не сработает.
Теоретически, нам не понадобится этот сервис во время тестирования, так как мы тестируем данные в памяти. Поэтому я пошел дальше и удалил его из `IntegrationTestBase, который предоставляет экземпляр HttpClient для наших тестов.
protected HttpClient GetClient()
{
var client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
var descriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(IHostedService) &&
d.ImplementationType.Name == "SessionCleanupHostedService");
if (descriptor != null)
{
services.Remove(descriptor);
}
/* removed for brevity */
});
});
/*some more things removed*/
return client;
}
Теперь это, скорее всего, избавит от исключений. Нет топлива, нет огня. Но это не объяснит причину этого поведения. Я бы предпочел сохранить эту услугу и найти решение проблемы. Самое простое исправление, о котором я сейчас могу подумать, - добавить пустую проверку перед созданием: