The article presents a method for eliminating the explicit injection of ILogger<T> in .NET services.
Abstract
The article discusses the benefits and drawbacks of eliminating the explicit injection of ILogger<T> in .NET services using a static logger factory. The author argues that this approach can make it easier to mock classes during testing and save a few lines of code, but it also hides a dependency and may make it harder to adjust the code when moving or copying the class to another repository. The author provides an example implementation of a static logger factory and shows how to use it in other classes.
Opinions
The author believes that the approach of using a static logger factory can have several benefits in comparison to the traditional Clean Code / Clean Architecture patterns.
The author acknowledges that the approach of using a static logger factory is not compliant with the traditional Clean Code / Clean Architecture patterns and that it hides a dependency.
The author argues that the benefits of using a static logger factory outweigh its drawbacks, provided that the solution is not part of a distributed environment like in microservices.
The author provides an example implementation of a static logger factory and shows how to use it in other classes.
The author compares the approach of using a static logger factory to the previous way of injecting the ILogger<T> and argues that the static method is more concise and easier to use.
The author encourages readers to share their thoughts on the approach in the comments and to check out the code on their GitHub.
The author concludes by thanking readers for taking the time to read the article and expressing their appreciation for their support and engagement.
How to Eliminate Injecting ILogger<T> Everywhere in .NET
Free all your services from this explicit injection
Dependency injection is great, it saves you a lot of manual set up for wiring up and creating instances, that are used accross your entire application. However, it comes with a catch. We are getting so used to simply adding a new dependency to yet another service, that we might overdo it a bit. Too many dependencies on a service are not clean and should be refactored. Nonetheless, there is always this one dependency, that is found in so many constructors, in so many projects out there — the ILogger<T>.
In this article, I am going to show you a way of at least eliminating this ILogger<T> from your constructors. This will not remove the dependency obviously, but it will remove one argument from the constructor, and thus makes it easier to mock your class during testing and it saves you one line of code per class.
Advisory Notice
The following approach certainly has its drawbacks and is NOT compliant with the traditional Clean Code / Clean Architecture patterns. Through removing the ILogger from the constructor, you are effectively hiding a dependency. This hidden dependency may take some more work to be adjusted, when moving or copying the class to another repository, that does not have access to the static logger factory. Furthermore, you may have to do extra work during testing and potentially confuse other programmers, who are not familiar with this approach. Use this solution only, when you are confident, that its benefits outweigh its drawbacks.
In my opinion, this approach can have several benefits in direct comparison to the “clean” way of injecting the ILogger in the constructor, provided that you have a single solution, that is not part of a distributed environment like in microservices.
You are able to use a logger, where it would otherwise only be possible when you pass it as an argument, e.g. static classes, methods and extension methods.
Testing is merely harder than with DI. Instead of passing an ILoggerFactory-Mock to the DI-Container, you pass it to the static logger factory, which costs you one line of code.
Changing the logger implementation only requires a change in the static logger factory, provided that the ILogger interface stays the same.
When a class cannot be instantiated by DI, you don’t have to think about how you get a logger for this class. E.g. a factory, where you inject ILoggerFactory.
“Clean” ways of removing the logger dependency, like the Decorator-Pattern, Dynamic Proxies or Middlewares only allow you to log before and after a method, not in between.
The Static LoggerFactory
The first step, to eliminate the injection of any type is probably keeping it anywhere as static. For the ILogger this can be achieved, by keeping an ILoggerFactory statically accessible. For this purpose, I created a static class called StaticLoggerFactory. This class holds a static ConcurrentDictionary<Type, ILogger>, which saves all the ILogger instances and makes them accessible concurrently. I also chose this class for the convenient method GetOrAdd(), which gives us a one liner for creating an instance, if it is not yet in the dictionary and returns one, if it is.
This class has to be initialized with an ILoggerFactory. You can get an instance of this from the IServiceProvider, after you have added Logging to your IServiceCollection.
Using the StaticLoggerFactory
That’s it for the setup and now we are going to have a look at how to use it in every other class:
Simply call the GetStaticLogger<T> function of our static logger factory and you are good to go. By the way this gets way easier, if you add the following line to your global usings (.NET 6 and above):
global using static <YourNameSpace>.StaticLoggerFactory;
This line enables you to simply call the static method without mentioning the class name StaticLoggerFactory, as you can see in the example above.
At last, let’s compare it to the previous way of injecting the ILogger<T>.
The injection and the null check is taking 92 additional characters, while the static method is only taking 35 characters (counted after the property name). So by using this method you can safe a few more lines of code and do not have to worry about creating multiple ILogger mocks in your tests anymore.
Let me know, what you think about this approach in the comments. Is it clean, or is it making things more complicated? Would you rather leave the dictionary away or keep it?
If you want to check out the code, you can find it on my github here:
Thank you for taking the time to read this article. I hope, you found it informative, educational and enjoyable.
Your support and engagement are greatly appreciated.
If you’re interested in staying up-to-date with the latest trends, tips, and tricks for clean architecture, clean coding, and the latest tech stacks — especially in the context of C#, .NET and Angular — I would appreciate it if you considered following me.
Have a wonderful day!
If you are not yet using Medium to grow your knowledge everyday, now is the perfect time to get started! With Medium, you can easily gain more knowledge on highly professional topics, publish high-quality content and reach a wider audience. To get started, simply create a Medium account using this link: