I've been making simple logger class with DI but I have some problem and question.
There is a problem. If I make a logger class with DI. I should use it like this whenever I use it.
var logger = new LogService(new FileLogger()); logger.WriteLine("message");
I want to make it to static class, but there is no way to inject dependency by constructor in static class.
So, I change it like this.
public static class LogService() { private static readonly ILoggable _logger; static LogService() { _logger = new FileLogger(); } }
I think this is so weird. This is not DI..
Isn't there a good way to inject dependency to static class?
You can use dependency injection in a static class using method or property injection. However, you cannot use constructor injection in a static class because the constructor of a static class cannot accept any parameters.
You can't inject a static logger. You have to either change it to an instance logger (if you can), or wrap it in an instance logger (that will call the static).
Types of Dependency Injection The injector class injects dependencies broadly in three ways: through a constructor, through a property, or through a method. Constructor Injection: In the constructor injection, the injector supplies the service (dependency) through the client class constructor.
Static classes are sealed and therefore cannot be inherited. They cannot inherit from any class except Object. Static classes cannot contain an instance constructor. However, they can contain a static constructor.
Dependency Injection, as a practice, is meant to introduce abstractions (or seams) to decouple volatile dependencies. A volatile dependency is a class or module that, among other things, can contain nondeterministic behavior or in general is something you which to be able to replace or intercept.
For a more detailed discussion about volatile dependencies, see section 1.3.2 of this freely readable introduction of my book.
Because your FileLogger
writes to disk, it contains nondeterministic behavior. For this reason you introduced the ILoggable
abstraction. This allows consumers to be decoupled from the FileLogger
implementation.
To be able to successfully decouple a consumer from its volatile dependency, however, you need to inject that dependency into the consumer. There are three common patterns to choose from:
Both Constructor Injection and Property Injection are applied inside the startup path of the application (a.k.a. the Composition Root) and require the consumer to store the dependency in a private field for later reuse. This requires the constructor and property to be instance members, i.e. non-static. Static constructors can't have any parameters and static properties lead to the Ambient Context anti-pattern (see section 5.3), and Temporal Coupling. This hinders testability and maintainability.
Method injection, on the other hand, is applied outside the Composition Root and it does not store any supplied dependency, but instead merely uses it.
Method injection is, therefore, the only of the three patterns that can be applied to both instance and static methods.
In that case, the method's consumer must supply the dependency. This does mean, however, that the consumer itself must have been supplied with that dependency either through constructor, property, or method injection.
Your example of the static LogService
that created FileLogger
inside its constructor is a great example of tightly coupled code. This is known as the Control Freak anti-pattern (section 5.1) or in general can be seen as a DIP violation. This is the opposite of DI.
To prevent tight coupling of volatile dependencies, the best is to make LogService
non-static and inject its volatile dependencies into its sole public constructor.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With