using System.Reflection; using GB5Shared.DTO.Framework.Login; using GB5Shared.Hosting; using GB5Shared.Promotion; using GB5Shared.ScanIdentifier; using Microsoft.AspNetCore.ResponseCompression; using Newtonsoft.Json; using WiBLL.Common; using WiSL.EndPoints.Hubs; namespace WiSL; public class WiModule : IModule { public string DisplayName => "WorkInstruction"; public void Register(IServiceCollection services, IConfiguration configuration) { services.AddHttpContextAccessor(); // Metadata Promotion — registered locally in this module's own process (EngagementHost), // resolved by EntityTypeCode via the exact same keyed-DI pattern FrameworkSL uses for its // own (in-process) entities. FrameworkBLL's RemoteHttpPromotionAdapter reaches these over // HTTP via the PromotionBridge endpoints in WiSL/EndPoints/Promotion — see // GB5Shared/DTO/Promotion/PromotionBridgeDTOs.cs for why a direct reference isn't used. services.AddKeyedScoped("WIPROCESS"); services.AddKeyedScoped("WISUBPROCESS"); services.AddKeyedScoped("WORKINSTRUCTION"); services.AddSignalR(options => { options.EnableDetailedErrors = true; options.KeepAliveInterval = TimeSpan.FromSeconds(15); options.ClientTimeoutInterval = TimeSpan.FromSeconds(60); options.MaximumReceiveMessageSize = 32 * 1024; }); services.AddSingleton(); // Generic Scan-Identifier & Label Platform (GB5Shared/ScanIdentifier) — the dispatcher + // engine only; WI's own IScanTargetResolver implementations are picked up by the // WIBLL/WIDAL services.Scan() call below already. This is the actual DI entry point // EngagementHost calls for this module (via EngagementHost.ModuleLoader) — WiSL/Program.cs's // own builder.Services calls are a separate path only exercised when WiSL runs standalone, // not when aggregated into EngagementHost, which is why these two registrations belonged // here and were silently missing from the running deployment. services.AddScanTargetResolvers(); services.AddScoped(); services.AddScoped(sp => { var ctx = sp.GetRequiredService().HttpContext; if (ctx != null && ctx.Request.Headers.TryGetValue("Login", out var header)) return JsonConvert.DeserializeObject(header!)!; return new LoginDTO(); }); services.AddResponseCompression(o => { o.EnableForHttps = true; o.Providers.Add(); }); var wiBLL = Assembly.Load("WIBLL"); var wiDAL = Assembly.Load("WIDAL"); services.Scan(scan => scan .FromAssemblies(wiBLL, wiDAL) .AddClasses(c => c.Where(t => !t.IsAbstract && !t.IsInterface && (t.Namespace == null || !t.Namespace.Contains(".DTO")))) .AsImplementedInterfaces() .WithScopedLifetime()); // WiExecutionBLL constructor-injects GB5Shared.ValidationCriteria.IValidationCriteriaEvaluator // (Quiz/Assessment eligibility gating) — that type isn't in WIBLL/WIDAL, so the scan above // never registers it. Explicit here rather than scanning all of GB5Shared, since GB5Shared // is a large grab-bag assembly and this is the only type WI needs from it via DI. services.AddScoped(); services.AddScoped(); } public void MapEndpoints(WebApplication app) { app.MapHub("/hubs/wi"); } }