Fix DbContext concurrency error in parallel company verification

Use IDbContextFactory pattern to create isolated DbContext instances
for each cache operation, making parallel verification thread-safe.

Changes:
- Add IDbContextFactory<ApplicationDbContext> registration
- Update CompanyVerifierService to use factory for cache operations
- Update tests with InMemoryDatabaseRoot for shared test data

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-20 16:54:58 +01:00
parent f1ccd217d8
commit 04a7c3628a
3 changed files with 50 additions and 13 deletions

View File

@@ -14,7 +14,7 @@ namespace TrueCV.Infrastructure.Services;
public sealed class CompanyVerifierService : ICompanyVerifierService
{
private readonly CompaniesHouseClient _companiesHouseClient;
private readonly ApplicationDbContext _dbContext;
private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
private readonly ILogger<CompanyVerifierService> _logger;
private const int FuzzyMatchThreshold = 70;
@@ -22,11 +22,11 @@ public sealed class CompanyVerifierService : ICompanyVerifierService
public CompanyVerifierService(
CompaniesHouseClient companiesHouseClient,
ApplicationDbContext dbContext,
IDbContextFactory<ApplicationDbContext> dbContextFactory,
ILogger<CompanyVerifierService> logger)
{
_companiesHouseClient = companiesHouseClient;
_dbContext = dbContext;
_dbContextFactory = dbContextFactory;
_logger = logger;
}
@@ -123,8 +123,11 @@ public sealed class CompanyVerifierService : ICompanyVerifierService
{
var cutoffDate = DateTime.UtcNow.AddDays(-CacheExpirationDays);
// Use factory to create a new DbContext for thread-safe parallel access
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
// Get recent cached companies
var cachedCompanies = await _dbContext.CompanyCache
var cachedCompanies = await dbContext.CompanyCache
.Where(c => c.CachedAt >= cutoffDate)
.ToListAsync();
@@ -160,7 +163,10 @@ public sealed class CompanyVerifierService : ICompanyVerifierService
private async Task CacheCompanyAsync(CompaniesHouseSearchItem item)
{
var existingCache = await _dbContext.CompanyCache
// Use factory to create a new DbContext for thread-safe parallel access
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
var existingCache = await dbContext.CompanyCache
.FirstOrDefaultAsync(c => c.CompanyNumber == item.CompanyNumber);
if (existingCache is not null)
@@ -183,10 +189,10 @@ public sealed class CompanyVerifierService : ICompanyVerifierService
CachedAt = DateTime.UtcNow
};
_dbContext.CompanyCache.Add(cacheEntry);
dbContext.CompanyCache.Add(cacheEntry);
}
await _dbContext.SaveChangesAsync();
await dbContext.SaveChangesAsync();
}
private static CompanyVerificationResult CreateVerificationResult(