Files
Nexus.Reader/src/.documentation/cache/summaries/NexusReader.Infrastructure__DependencyInjection.cs.json
T
2026-05-25 14:02:56 +02:00

1 line
16 KiB
JSON

{"path":"NexusReader.Infrastructure/DependencyInjection.cs","purpose":"Registers and configures infrastructure services (persistence, external clients, AI/embedding, background jobs, authorization, and scoped application services) into the DI container for the NexusReader application.","classification":{"role":"service-registration","layer":"infrastructure","confidence":0.95,"evidence":["Service registration API usage","Defines AddInfrastructure(this IServiceCollection, IConfiguration) that calls many services.Add... registrations","Configures DbContexts, external clients (Qdrant, Neo4j), Hangfire, AI clients, and application service bindings"]},"className":"DependencyInjection","methods":[{"name":"AddInfrastructure","line":31,"endLine":134,"signature":"(this IServiceCollection services, IConfiguration configuration) -> IServiceCollection","purpose":"Configures and registers persistence (Postgres/Sqlite), external clients (Qdrant, Neo4j), Hangfire, AI settings/pipelines/clients, application service implementations, and authorization into the DI container.","calls":[{"targetFile":"NexusReader.Data.Persistence/AppDbContext.cs","targetMethod":"AppDbContext (type)","callLine":36,"paramSummary":"pgConnectionString or sqliteConnectionString passed into UseNpgsql/UseSqlite options"},{"targetFile":"unknown","targetMethod":"AddDbContextFactory<T>","callLine":36,"paramSummary":"AppDbContext factory with DB provider options"},{"targetFile":"unknown","targetMethod":"AddDbContext<T>","callLine":41,"paramSummary":"AppDbContext with DB provider options"},{"targetFile":"unknown","targetMethod":"AddSingleton<QdrantClient>","callLine":57,"paramSummary":"new QdrantClient(new Uri(qdrantUrl))"},{"targetFile":"unknown","targetMethod":"GraphDatabase.Driver","callLine":61,"paramSummary":"neo4jUrl, AuthTokens.None"},{"targetFile":"unknown","targetMethod":"AddHangfire","callLine":66,"paramSummary":"Configure Hangfire with PostgreSqlStorage using pgConnectionString"},{"targetFile":"unknown","targetMethod":"AddHangfireServer","callLine":69,"paramSummary":"start Hangfire server"},{"targetFile":"NexusReader.Infrastructure/Configuration/AiSettings.cs","targetMethod":"AiSettings (type)","callLine":72,"paramSummary":"Configure and bind AiSettings from configuration"},{"targetFile":"unknown","targetMethod":"Get<T> (IConfigurationSection)","callLine":74,"paramSummary":"Bind AiSettings section into AiSettings instance"},{"targetFile":"unknown","targetMethod":"AddResiliencePipeline","callLine":81,"paramSummary":"define 'ai-retry' retry strategy with PredicateBuilder"},{"targetFile":"unknown","targetMethod":"ShouldHandle predicate","callLine":85,"paramSummary":"exception -> check message contains 429/Too Many Requests/quota/503/ServiceUnavailable/demand"},{"targetFile":"unknown","targetMethod":"AddChatClient","callLine":99,"paramSummary":"GeminiChatClient configured with AiSettings.ApiKey and ModelId"},{"targetFile":"unknown","targetMethod":"AddEmbeddingGenerator","callLine":105,"paramSummary":"GeminiEmbeddingGenerator configured with ApiKey and EmbeddingModel"},{"targetFile":"NexusReader.Infrastructure/Services/KnowledgeService.cs","targetMethod":"KnowledgeService (type)","callLine":112,"paramSummary":"registered as IKnowledgeService (scoped)"},{"targetFile":"NexusReader.Infrastructure/Services/EpubReaderService.cs","targetMethod":"EpubReaderService (type)","callLine":113,"paramSummary":"registered as IEpubReader (transient)"},{"targetFile":"NexusReader.Infrastructure/Services/EpubMetadataExtractor.cs","targetMethod":"EpubMetadataExtractor (type)","callLine":114,"paramSummary":"registered as IEpubMetadataExtractor (transient)"},{"targetFile":"NexusReader.Infrastructure/Services/BookStorageService.cs","targetMethod":"BookStorageService (type)","callLine":118,"paramSummary":"registered as IBookStorageService (scoped)"},{"targetFile":"NexusReader.Infrastructure/Persistence/EbookRepository.cs","targetMethod":"EbookRepository (type)","callLine":121,"paramSummary":"registered as IEbookRepository (scoped)"},{"targetFile":"NexusReader.Infrastructure/RealTime/SignalRSyncBroadcaster.cs","targetMethod":"SignalRSyncBroadcaster (type)","callLine":124,"paramSummary":"registered as ISyncBroadcaster (scoped)"},{"targetFile":"NexusReader.Application.Security.Authorization/ProUserHandler.cs","targetMethod":"ProUserHandler (type)","callLine":131,"paramSummary":"registered as IAuthorizationHandler (scoped)"},{"targetFile":"self","targetMethod":"InfrastructureMarker (type)","callLine":132,"paramSummary":"registered as IInfrastructureMarker (scoped)"}],"actions":[{"id":"branch_33","kind":"branch","label":"Choose DB provider (Postgres vs Sqlite)","line":33,"detail":"if Postgres connection string present use Npgsql; else use Sqlite. Registers both AddDbContextFactory and AddDbContext in each branch","visibility":"detail-only","confidence":0.7},{"id":"addinfrastructure_branch_34_0","kind":"branch","label":"Evaluates branch condition","line":34,"detail":"if (!string.IsNullOrEmpty(pgConnectionString))","conditionSummary":"!string.IsNullOrEmpty(pgConnectionString)","outcomeLabels":["true","false"],"visibility":"secondary-visible","confidence":0.78},{"id":"service-registration_36","kind":"mapping","label":"Register AppDbContext factory scoped","line":36,"detail":"services.AddDbContextFactory<AppDbContext>(..., ServiceLifetime.Scoped)","visibility":"detail-only","confidence":0.7},{"id":"service-registration_41","kind":"mapping","label":"Register AppDbContext scoped","line":41,"detail":"services.AddDbContext<AppDbContext>(...)","visibility":"detail-only","confidence":0.7},{"id":"addinfrastructure_fallback_44_1","kind":"fallback","label":"Falls back to alternate path","line":44,"detail":"else","outcomeLabels":["fallback"],"visibility":"primary-visible","confidence":0.84},{"id":"service-registration_57","kind":"mapping","label":"Register Qdrant client singleton","line":57,"detail":"services.AddSingleton<QdrantClient>(...)","visibility":"detail-only","confidence":0.7},{"id":"service-registration_61","kind":"mapping","label":"Register Neo4j driver singleton","line":61,"detail":"services.AddSingleton<IDriver>(...)","visibility":"detail-only","confidence":0.7},{"id":"branch_64","kind":"branch","label":"Conditional Hangfire registration","line":64,"detail":"If Postgres present, configures Hangfire with PostgreSqlStorage and starts server","conditionSummary":"pgConnectionString not empty","outcomeLabels":["register hangfire","skip hangfire"],"visibility":"detail-only","confidence":0.7},{"id":"addinfrastructure_branch_64_2","kind":"branch","label":"Evaluates branch condition","line":64,"detail":"if (!string.IsNullOrEmpty(pgConnectionString))","conditionSummary":"!string.IsNullOrEmpty(pgConnectionString)","outcomeLabels":["true","false"],"visibility":"secondary-visible","confidence":0.78},{"id":"configure_72","kind":"mapping","label":"Bind configuration sections","line":72,"detail":"services.Configure<AiSettings>, services.Configure<StripeSettings>, binds AiSettings instance","visibility":"detail-only","confidence":0.7},{"id":"addinfrastructure_branch_76_3","kind":"branch","label":"Evaluates branch condition","line":76,"detail":"if (string.IsNullOrWhiteSpace(aiSettings.ApiKey) || aiSettings.ApiKey == \"PLACEHOLDER\")","conditionSummary":"string.IsNullOrWhiteSpace(aiSettings.ApiKey) || aiSettings.ApiKey == \"PLACEHOLDER\"","outcomeLabels":["true","false"],"visibility":"secondary-visible","confidence":0.78},{"id":"guard-clause_76","kind":"guard-clause","label":"Warns if AI API key missing/placeholder","line":76,"detail":"Console.WriteLine warning about missing AI API key","conditionSummary":"aiSettings.ApiKey null/whitespace or 'PLACEHOLDER'","outcomeLabels":["warning","continue"],"visibility":"detail-only","confidence":0.7},{"id":"retry-policy_81","kind":"mapping","label":"AI retry resilience pipeline","line":81,"detail":"Adds exponential backoff retry with jitter, max attempts from AiSettings, and custom ShouldHandle predicate checking exception messages (lines 85-91)","visibility":"detail-only","confidence":0.7},{"id":"service-registration_99","kind":"mapping","label":"Register AI chat and embedding clients","line":99,"detail":"services.AddChatClient and services.AddEmbeddingGenerator using Gemini clients configured with AiSettings","visibility":"detail-only","confidence":0.7},{"id":"service-registration_111","kind":"mapping","label":"Register application-layer services","line":111,"detail":"Registers KnowledgeService, EpubReaderService, EpubMetadataExtractor, BookStorageService, EbookRepository, SignalRSyncBroadcaster with appropriate lifetimes (scoped/transient)","visibility":"detail-only","confidence":0.7},{"id":"authorization_126","kind":"mapping","label":"Add 'ProUser' policy and handler","line":126,"detail":"options.AddPolicy('ProUser', ...); registers ProUserHandler as IAuthorizationHandler","visibility":"detail-only","confidence":0.7},{"id":"return_134","kind":"return","label":"Return modified IServiceCollection","line":134,"detail":"returns services to allow chaining","visibility":"detail-only","confidence":0.7},{"id":"addinfrastructure_return_134_4","kind":"return","label":"Returns result","line":134,"detail":"return services;","visibility":"detail-only","confidence":0.7}]},{"name":"Assembly","line":137,"endLine":137,"signature":"get Assembly() -> System.Reflection.Assembly","purpose":"Exposes the assembly that contains the DependencyInjection class (useful for scanning/registration reflection).","calls":[{"targetFile":"self","targetMethod":"typeof(DependencyInjection).Assembly","callLine":137,"paramSummary":"reflection on DependencyInjection type"}],"actions":[{"id":"expose-metadata_137","kind":"mapping","label":"Return assembly for scanning","line":137,"detail":"Used externally to locate types in this assembly","visibility":"detail-only","confidence":0.7}]}],"types":[{"name":"DependencyInjection","kind":"type-alias","line":29,"purpose":"Static DI composition class that exposes AddInfrastructure and the assembly reference for the Infrastructure layer.","fields":[]},{"name":"IInfrastructureMarker","kind":"interface","line":140,"purpose":"Marker interface used to identify the infrastructure assembly/types during scanning or composition.","fields":[]},{"name":"InfrastructureMarker","kind":"model","line":141,"purpose":"Internal concrete implementation of IInfrastructureMarker used solely to register a marker instance in DI.","fields":[]}],"serviceRegistrations":[{"line":36,"serviceType":"AppDbContext (factory)","implementationType":"AppDbContext","lifetime":"scoped","factorySummary":"Registers AppDbContext factory configured with Npgsql or Sqlite depending on connection string"},{"line":41,"serviceType":"AppDbContext","implementationType":"AppDbContext","lifetime":"scoped","factorySummary":"Registers a scoped AppDbContext for repositories that require a direct DbContext"},{"line":57,"serviceType":"QdrantClient","implementationType":"QdrantClient","lifetime":"singleton","factorySummary":"Singleton Qdrant client created with configured URL"},{"line":61,"serviceType":"IDriver","implementationType":"Neo4j.Driver","lifetime":"singleton","factorySummary":"Neo4j driver singleton created with GraphDatabase.Driver"},{"line":66,"serviceType":"Hangfire (server & storage)","implementationType":"Hangfire with Postgres storage","lifetime":"unknown","factorySummary":"Configures Hangfire to use Postgres storage when Postgres connection string is available and starts a Hangfire server"},{"line":99,"serviceType":"ChatClient","implementationType":"GeminiChatClient","lifetime":"unknown","factorySummary":"Registers chat client configured with AiSettings"},{"line":105,"serviceType":"EmbeddingGenerator","implementationType":"GeminiEmbeddingGenerator","lifetime":"unknown","factorySummary":"Registers embedding generator configured with AiSettings"},{"line":112,"serviceType":"IKnowledgeService","implementationType":"KnowledgeService","lifetime":"scoped","factorySummary":"Application service providing knowledge features"},{"line":113,"serviceType":"IEpubReader","implementationType":"EpubReaderService","lifetime":"transient","factorySummary":"Epub reading implementation"},{"line":114,"serviceType":"IEpubMetadataExtractor","implementationType":"EpubMetadataExtractor","lifetime":"transient","factorySummary":"Extracts epub metadata"},{"line":118,"serviceType":"IBookStorageService","implementationType":"BookStorageService","lifetime":"scoped","factorySummary":"Storage service registered scoped to respect environment-specific path resolution (MAUI fix)"},{"line":121,"serviceType":"IEbookRepository","implementationType":"EbookRepository","lifetime":"scoped","factorySummary":"Repository for ebook entities (scoped to match AppDbContext)"},{"line":124,"serviceType":"ISyncBroadcaster","implementationType":"SignalRSyncBroadcaster","lifetime":"scoped","factorySummary":"SignalR-based broadcaster for real-time sync (scoped wrapper around IHubContext)"},{"line":131,"serviceType":"IAuthorizationHandler","implementationType":"ProUserHandler","lifetime":"scoped","factorySummary":"Handler that enforces 'ProUser' authorization requirement"},{"line":132,"serviceType":"IInfrastructureMarker","implementationType":"InfrastructureMarker","lifetime":"scoped","factorySummary":"Registers internal marker for infrastructure assembly scanning"}],"startupActions":[],"dependencies":["NexusReader.Data.Persistence/AppDbContext.cs","NexusReader.Infrastructure/Services/KnowledgeService.cs","NexusReader.Infrastructure/Services/EpubReaderService.cs","NexusReader.Infrastructure/Services/EpubMetadataExtractor.cs","NexusReader.Infrastructure/Services/BookStorageService.cs","NexusReader.Infrastructure/Persistence/EbookRepository.cs","NexusReader.Infrastructure/RealTime/SignalRSyncBroadcaster.cs","NexusReader.Application.Security.Authorization/ProUserHandler.cs"],"patterns":["Dependency Injection","Repository","Marker Interface"],"domainConcepts":["Ebook","AI (chat & embeddings)","Persistence (Postgres/Sqlite)","Background jobs (Hangfire)","Graph DB (Neo4j)","Vector DB (Qdrant)"],"keyDetails":"Conditional DB provider selection (Postgres vs Sqlite), conditional Hangfire registration when Postgres present, AI resilience pipeline with custom exception predicate and configurable retry attempts, AI API key presence warning, multiple scoped service registrations to align with AppDbContext lifetime.","orchestrationMethods":[{"name":"AddInfrastructure","line":31,"confidence":0.98,"reason":"Coordinates 21 downstream calls with 6 architectural actions.","actionKinds":["branch","mapping","fallback","guard-clause","return"],"evidencePaths":["NexusReader.Infrastructure/DependencyInjection.cs","NexusReader.Data.Persistence/AppDbContext.cs","unknown","unknown","unknown","unknown"]}],"typedContracts":[{"name":"IInfrastructureMarker","kind":"interface","line":140,"fieldCount":0,"evidencePaths":["NexusReader.Infrastructure/DependencyInjection.cs"]},{"name":"InfrastructureMarker","kind":"model","line":141,"fieldCount":0,"evidencePaths":["NexusReader.Infrastructure/DependencyInjection.cs"]}],"persistenceInteractions":[],"externalInteractions":[],"evidenceAnchors":[{"kind":"orchestration-method","label":"AddInfrastructure","line":31,"summary":"Coordinates 21 downstream calls with 6 architectural actions.","confidence":0.98,"evidencePaths":["NexusReader.Infrastructure/DependencyInjection.cs","NexusReader.Data.Persistence/AppDbContext.cs","unknown","unknown","unknown","unknown"]},{"kind":"typed-contract","label":"IInfrastructureMarker","line":140,"summary":"interface with 0 fields.","confidence":0.8,"evidencePaths":["NexusReader.Infrastructure/DependencyInjection.cs"]},{"kind":"typed-contract","label":"InfrastructureMarker","line":141,"summary":"model with 0 fields.","confidence":0.8,"evidencePaths":["NexusReader.Infrastructure/DependencyInjection.cs"]}],"cacheMetadata":{"schemaVersion":2,"analysisVersion":"2026-05-23.cache-v1","contentChecksum":"7c77036d065eac022da2a4e638a72a054ff7c62d068764e54988f237b272ff8a","sourceByteSize":6116,"analyzedAt":"2026-05-23T16:21:23.367Z","technology":"dotnet"}}