I followed @Olaf Svenson’s recipe for replicating the problem, which I have been able to replicate. I then tried @Martin’s approach by making the MyOptionsValidator aware that it needs a ILoggerFactory but it still resulted in a circular dependency scenario (somehow).

I think there should be a way to log unhandled exceptions so that you don’t need to log anything in your MyOptionsValidator but rather let it return a failure result which would result in an exception being thrown and that be logged instead. But for Worker Services this seems to be an issue? Let’s assume we cannot do this, then look at the solution I provided below…

(UPDATE: You actually don’t even need to do this below but it was a cool challenge nonetheless. Don’t log within your Validator. This will prevent the unnecessary complexity. The normal unhandled exception logging process will kick in and actually log to the other loggers when your logger’s config is invalid. Super simple and very effective. Now you can have all kinds of loggers take care of this concern for you.)

My thinking is that this problem is a complicated one and you need to move the complexity to the DI space where it belongs (since that’s where all the components and dependencies are wired up that caused this to happen) so that any new Validator that you write will not have to be aware of this “circular dependency” problem of a given logger that you want to inject.

The one method I tried to address this with is to create a Fall-back logger. Now I’m in no way saying my approach is the defacto standard but it solved the problem and since it should only run once (since the MyOptionsValidator is setup as a singleton) you don’t have to worry about any performance hits at runtime.

I changed the code that did this:

public static IServiceCollection AddMyLib(this IServiceCollection services) =>
            services.AddSingleton<IValidateOptions<MyOptions>, MyOptionsValidator>();

To do this:

public static IServiceCollection AddMyLib(this IServiceCollection services) =>
            services.AddSingleton<IValidateOptions<MyOptions>, MyOptionsValidator>(
                sp => new MyOptionsValidator(CreateFallback<IValidateOptions<MyOptions>>()));

public static ILogger<T> CreateFallback<T>()
{
    return LoggerFactory.Create(x => x.AddConsole()).CreateLogger<T>();
}

I am not sure how to inject a secondary ILoggerFactory using the .NET Core DI infrastructure. Maybe you could create a wrapper class and use an embedded instance of a LoggerFactory and then resolve that wrapper class everywhere you would like to use the Fall-back logger?

You have to setup a separate LoggerFactory instance to make sure you don’t expose the FileLogger that can cause the problem. This does mean that your AddMyLib extension method would have to move somewhere where you are happy to pull in the Microsoft.Extensions.Logging package (and whatever logger package you wish to use in the process) unless you can make use of the wrapper solution I mentioned (using an abstraction of course).

So if your app is incorrectly configured, it will log the configuration error and the app will stop running since the MyOptionsValidator causes an exception to be raised.

Logging the configuration error and displaying the exception

But if your app is correctly configured…

App is running happily

CLICK HERE to find out more related problems solutions.

Leave a Comment

Your email address will not be published.

Scroll to Top