Files
RealCV/src/RealCV.Infrastructure/DependencyInjection.cs
Peter Foster 998e9a8ab8 Rename project to RealCV with new logo and font updates
- Rename all TrueCV references to RealCV across the codebase
- Add new transparent RealCV logo
- Switch from JetBrains Mono to Inter font for better number clarity
- Update solution, project files, and namespaces

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 15:07:20 +00:00

147 lines
5.8 KiB
C#

using Hangfire;
using Hangfire.SqlServer;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Polly;
using Polly.Extensions.Http;
using Stripe;
using RealCV.Application.Interfaces;
using RealCV.Infrastructure.Configuration;
using RealCV.Infrastructure.Data;
using RealCV.Infrastructure.ExternalApis;
using RealCV.Infrastructure.Jobs;
using RealCV.Infrastructure.Services;
namespace RealCV.Infrastructure;
public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
{
// Configure DbContext with SQL Server
// AddDbContextFactory enables thread-safe parallel operations
services.AddDbContextFactory<ApplicationDbContext>(options =>
options.UseSqlServer(
configuration.GetConnectionString("DefaultConnection"),
sqlOptions =>
{
sqlOptions.EnableRetryOnFailure(
maxRetryCount: 3,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null);
}));
// Also register DbContext for scoped injection (non-parallel scenarios)
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
configuration.GetConnectionString("DefaultConnection"),
sqlOptions =>
{
sqlOptions.EnableRetryOnFailure(
maxRetryCount: 3,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null);
}));
// Configure Hangfire with SQL Server storage
services.AddHangfire(config => config
.SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseSqlServerStorage(
configuration.GetConnectionString("HangfireConnection"),
new SqlServerStorageOptions
{
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
QueuePollInterval = TimeSpan.Zero,
UseRecommendedIsolationLevel = true,
DisableGlobalLocks = true
}));
services.AddHangfireServer();
// Configure options
services.Configure<CompaniesHouseSettings>(
configuration.GetSection(CompaniesHouseSettings.SectionName));
services.Configure<AnthropicSettings>(
configuration.GetSection(AnthropicSettings.SectionName));
services.Configure<AzureBlobSettings>(
configuration.GetSection(AzureBlobSettings.SectionName));
services.Configure<LocalStorageSettings>(
configuration.GetSection(LocalStorageSettings.SectionName));
services.Configure<StripeSettings>(
configuration.GetSection(StripeSettings.SectionName));
// Configure Stripe API key
var stripeSettings = configuration.GetSection(StripeSettings.SectionName).Get<StripeSettings>();
if (!string.IsNullOrEmpty(stripeSettings?.SecretKey))
{
StripeConfiguration.ApiKey = stripeSettings.SecretKey;
}
// Configure HttpClient for CompaniesHouseClient with retry policy
services.AddHttpClient<CompaniesHouseClient>((serviceProvider, client) =>
{
var settings = configuration
.GetSection(CompaniesHouseSettings.SectionName)
.Get<CompaniesHouseSettings>();
if (settings is not null)
{
client.BaseAddress = new Uri(settings.BaseUrl);
}
})
.AddPolicyHandler(GetRetryPolicy());
// Register services
services.AddScoped<ICVParserService, CVParserService>();
services.AddScoped<ICompanyNameMatcherService, AICompanyNameMatcherService>();
services.AddScoped<ICompanyVerifierService, CompanyVerifierService>();
services.AddScoped<IEducationVerifierService, EducationVerifierService>();
services.AddScoped<ITimelineAnalyserService, TimelineAnalyserService>();
services.AddScoped<ICVCheckService, CVCheckService>();
services.AddScoped<IUserContextService, UserContextService>();
services.AddScoped<IAuditService, AuditService>();
services.AddScoped<IStripeService, StripeService>();
services.AddScoped<ISubscriptionService, Services.SubscriptionService>();
// Register file storage - use local storage if configured, otherwise Azure
var useLocalStorage = configuration.GetValue<bool>("UseLocalStorage");
if (useLocalStorage)
{
services.AddScoped<IFileStorageService, LocalFileStorageService>();
}
else
{
services.AddScoped<IFileStorageService, FileStorageService>();
}
// Register Hangfire jobs
services.AddTransient<ProcessCVCheckJob>();
services.AddTransient<ResetMonthlyUsageJob>();
return services;
}
private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
onRetry: (outcome, timespan, retryAttempt, context) =>
{
// Logging could be added here via ILogger if injected
});
}
}