The application was failing at startup with the following error:
System.InvalidOperationException: A suitable constructor for type 'SecureBootWatcher.Client.Services.ClientUpdateService' could not be located.
Ensure the type is concrete and all parameters of a public constructor are either registered as services or passed as arguments.
In Program.cs, the ClientUpdateService was being registered twice in conflicting ways:
// Line 215 - Correct registration
services.AddSingleton<IClientUpdateService, ClientUpdateService>();
// Line 216 - INCORRECT registration
services.AddHttpClient<IClientUpdateService, ClientUpdateService>();The AddHttpClient<TClient, TImplementation>() method is used when you want the DI container to create an HTTP client that implements the interface. However, ClientUpdateService is not an HTTP client itself - it uses IHttpClientFactory to create HTTP clients.
The correct pattern is:
- Register
IHttpClientFactoryviaAddHttpClient()(already done on line 205) - Register
ClientUpdateServiceas a singleton that consumesIHttpClientFactory
Removed the duplicate/incorrect registration:
services.AddHttpClient("SecureBootIngestion");
services.AddSecureBootWatcherOptions(configuration);
services.AddSingleton<IRegistrySnapshotProvider, RegistrySnapshotProvider>();
services.AddSingleton<IEventLogReader, EventLogReader>();
services.AddSingleton<IEventCheckpointStore, FileEventCheckpointStore>();
services.AddSingleton<ISecureBootCertificateEnumerator, PowerShellSecureBootCertificateEnumerator>();
// Register Client Update Service
services.AddSingleton<IClientUpdateService, ClientUpdateService>(); // ? Correct
services.AddHttpClient<IClientUpdateService, ClientUpdateService>(); // ? WRONG - Causes error
services.AddSingleton<IReportBuilder, ReportBuilder>();
services.AddSingleton<SecureBootWatcherService>();services.AddHttpClient("SecureBootIngestion");
services.AddSecureBootWatcherOptions(configuration);
services.AddSingleton<IRegistrySnapshotProvider, RegistrySnapshotProvider>();
services.AddSingleton<IEventLogReader, EventLogReader>();
services.AddSingleton<IEventCheckpointStore, FileEventCheckpointStore>();
services.AddSingleton<ISecureBootCertificateEnumerator, PowerShellSecureBootCertificateEnumerator>();
// Register Client Update Service (needs IHttpClientFactory, so register after AddHttpClient)
services.AddSingleton<IClientUpdateService, ClientUpdateService>(); // ? Only registration needed
services.AddSingleton<IReportBuilder, ReportBuilder>();
services.AddSingleton<SecureBootWatcherService>();The ClientUpdateService constructor requires three dependencies:
public ClientUpdateService(
ILogger<ClientUpdateService> logger, // ? Registered via AddLogging()
IHttpClientFactory httpClientFactory, // ? Registered via AddHttpClient()
IOptions<SecureBootWatcherOptions> options) // ? Registered via AddSecureBootWatcherOptions()
{
_logger = logger;
_httpClientFactory = httpClientFactory;
_options = options;
}All three dependencies are properly registered in the DI container, so the service can now be constructed successfully.
Build succeeded.
5 Warning(s)
0 Error(s)
[16:56:01 INF] ========================================
[16:56:01 INF] SecureBootWatcher Client Starting
[16:56:01 INF] ========================================
[16:56:01 INF] Version: 1.3.39+d1ccd76aa1
[16:56:01 INF] Base Directory: C:\Users\...\SecureBootWatcher.Client\bin\Debug\net48\
[16:56:01 INF] Log File Path: ...\logs\client-.log
[16:56:01 INF] Log Format: CMTrace
[16:56:01 INF] Rolling Interval: Day
[16:56:01 INF] Fleet ID: mslabs
[16:56:01 INF] Active Sinks: AzureQueue, WebApi
? Client starts successfully without DI errors!
- Use
AddHttpClient()to registerIHttpClientFactory - Use
AddSingleton<TInterface, TImplementation>()for services that consumeIHttpClientFactory - Inject
IHttpClientFactoryinto your service constructor - Create HTTP clients using
_httpClientFactory.CreateClient()or_httpClientFactory.CreateClient("name")
- Don't use
AddHttpClient<TClient, TImplementation>()for services that needIHttpClientFactory - Don't register the same service twice with different methods
- Don't confuse "typed HTTP clients" with "services that use HTTP clients"
Use AddHttpClient<TClient, TImplementation>() only when:
- The client IS the service (not just uses HTTP)
- The client's constructor only needs
HttpClient(no other dependencies)
Example (not our case):
public class GitHubApiClient
{
private readonly HttpClient _httpClient;
public GitHubApiClient(HttpClient httpClient) // Only HttpClient!
{
_httpClient = httpClient;
}
}
// Registration
services.AddHttpClient<GitHubApiClient>(client =>
{
client.BaseAddress = new Uri("https://api.github.com");
});Our ClientUpdateService needs multiple dependencies, so we use the factory pattern:
public class ClientUpdateService : IClientUpdateService
{
private readonly ILogger<ClientUpdateService> _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IOptions<SecureBootWatcherOptions> _options;
public ClientUpdateService(
ILogger<ClientUpdateService> logger,
IHttpClientFactory httpClientFactory, // Factory injected
IOptions<SecureBootWatcherOptions> options)
{
_logger = logger;
_httpClientFactory = httpClientFactory;
_options = options;
}
public async Task<UpdateCheckResult> CheckForUpdateAsync(CancellationToken cancellationToken)
{
// Create HTTP client when needed
var client = _httpClientFactory.CreateClient();
client.BaseAddress = _options.Value.Sinks.WebApi.BaseAddress;
// Use client...
}
}
// Registration
services.AddHttpClient(); // Register factory
services.AddSingleton<IClientUpdateService, ClientUpdateService>(); // Register serviceThis pattern allows:
- ? Multiple HTTP clients in the same service
- ? Other dependencies (logger, options)
- ? Full control over HTTP client configuration
- ? Named HTTP clients if needed
- Microsoft Docs: Use IHttpClientFactory to implement resilient HTTP requests
- Client Update Feature:
docs\CLIENT_VERSION_TRACKING.md - DI in .NET: Dependency injection in .NET
Fix Summary:
- ? Removed duplicate
AddHttpClient<IClientUpdateService, ClientUpdateService>()registration - ? Kept single
AddSingleton<IClientUpdateService, ClientUpdateService>()registration - ? Client now starts without DI errors
- ? ClientUpdateService properly constructed with all dependencies
Status: FIXED AND VERIFIED ?