Search code examples
c#dependency-injection

How do I ensure that certain services are not overridden by user registrations


I know that lots of library code relies on TryAdd* and TryAddEnumerable methods to NOT overwrite user-supplied registrations for certain services and only provide default implementations if none have been registered.

However, what if I need to make sure that user code does not/cannot override my library's default service registrations? I've played around with some form of AsStrict and asked a question here, but this among other shortcomings relies on the user to implement it in his own code-base.

Is there a pattern or general way of structuring AddMyLibrary so that I can ensure that I have fixed dependencies that the user cannot provide their implementation for.


Solution

  • To ensure that your library's service registrations cannot be overridden by the user, you can use IHostedService. This approach runs validation at application startup and prevents the user from overriding your critical service registrations.

    your service :

    public interface IMyService
    {
        void Execute();
    }
    
    public class MyService : IMyService
    {
        public void Execute()
        {
            Console.WriteLine("Executing MyService");
        }
    }
    

    Hosted service for validation:

    internal class ValidationHostedService(IServiceProvider serviceProvider) : IHostedService
    {
        public Task StartAsync(CancellationToken cancellationToken)
        {
            // Validate required services at application startup
            var myService = serviceProvider.GetRequiredService<IMyService>();
            if (myService.GetType() != typeof(MyService))
            {
                throw new InvalidOperationException("IMyService cannot be overridden.");
            }
    
            return Task.CompletedTask;
        }
    
        public Task StopAsync(CancellationToken cancellationToken)
        {
            // No specific stop logic required
            return Task.CompletedTask;
        }
    }
    

    Service Collection Extension :

    public static class ServiceCollectionExtensions
    {
        public static IServiceCollection AddExampleLibrary(this IServiceCollection services)
        {
            // Register your library's default service
            services.AddSingleton<IMyService, MyService>();
    
            // Register a hosted service for automatic validation
            services.AddHostedService<ValidationHostedService>();
    
            return services;
        }
    }