Use Dependency Injection in Static Class

How to inject dependency to static class

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:

  • Constructor Injection—Dependencies are statically defined as list of parameters to the class's instance constructor.
  • Property Injection—Dependencies are injected into the consumer via writable instance properties.
  • Method Injection—Dependencies are injected into the consumer as method parameters.

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.

But this brings you back to the reason why you probably wanted to make that class static in the first place, which is that you have many classes that need to log. This, however, might be caused by another design issue in your code. To understand this, you might want to read through this q&a to get some sense of what design changes you can make that allows less classes to depend on your logger class.

Use dependency injection in static class

You basically have two options:

  1. Change the class from static to an instance class and supply the dependency through Constructor Injection.
  2. Supply the dependency to the class's public method through Method Injection.

Here are examples for each option.

Option 1. Change the class from static to an instance class

Change the class from static to an instance class and supply IConfiguracion through Constructor Injection. XHelper should in that case be injected into the constructor of its consumers. Example:

public class XHelper
{
private readonly IConfiguration config;

public XHelper(IConfiguration config)
{
this.config = config ?? throw new ArgumentNullException("config");
}

public TResponse Execute(string metodo, TRequest request)
{
string y = this.config.apiUrl;

return xxx;
}
}

2. Supply the IConfiguration to the Execute method through Method Injection.

Example:

public static class XHelper
{
public static TResponse Execute(
string metodo, TRequest request, IConfiguration config)
{
if (config is null) throw new ArgumentNullException("config");

string y = config.apiUrl;

return xxx;
}
}

Less favorable options

There are of course more options to consider, but I consider them all to be less favorable, because they would either cause code smells or anti-patterns.

For instance:

  • You might be inclined to make the DI Container accessible through a static method and call it from inside your static class, but this is an anti-pattern called Service Locator.
  • You could allow setting the static class's dependencies through static properties on that class, but this leads to the Ambient Context anti-pattern.
  • You could change the class to an instance class, but instead of using Constructor Injection make use of Property Injection, but this causes Temporal Coupling, which is a code smell.

Dependency injection with a static logger, static helper class

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). Also it is fairly hard to inject anything to a static class (because you don't control the static constructor in any way) - that's why I tend to pass all the objects I want to inject as parameters.

Static class to dependency injection

Problem

DI is not as simple as you've made it look. There are DI frameworks out there which take care of the DI concern, and they are mature pieces of software.

You can't really do DI yourself without designing a DI container because of the way DI should work

DI solves a few problems, a couple of the main ones are:

  • IoC - ensuring that components aren't tightly coupled by moving the resolution and provision of dependencies outside of the component classes

  • Lifetime scope - ensures that components have a well defined lifetime/lifecycle and that they are correctly instantiated and disposed of at key points in your application

How does it look?

You shouldn't even see the container! - you should only see components dependencies and the rest should look like magic...

DI containers should be very transparent. Your components and services should require their dependencies simply by specifying what the dependencies are (in their constructors)

What's my current problem?

You don't want to be having to manually wire up sub-dependencies with code like this:

public MainWindowViewModel(ILocalizer localizer)
{
_localizer = localizer;
UC1ViewModel = new UserControl1ViewModel(localizer); // <-- ouch
}

There are a number of problems with the above:

  1. You are making the MainWindowViewModel responsible for creating the UC1ViewModel and managing the lifetime of the object (this isn't always a bad thing as sometimes you want to manage the lifetime of an object in a particular component)

  2. You are coupling the implementation of the MainWindowViewModel to the constructor implementation of UserControl1ViewModel - if you require another dependency in UserControl1ViewModel, suddenly you have to update MainWindowViewModel to inject that dependency, cue a lot of refactoring. This is because you are instantiating the type yourself instead of letting a container do it.

How do containers prevent code like the above?

With any container you should be registering components

The container will track the list of possible components and services and use this registry to resolve dependencies.

It also tracks the dependencies lifecycle (singleton, instanced etc)

Ok I've registered everything, what next?

Once you have registered all your dependencies, you then resolve your root component from the container. This is known as the composition root and should be the 'entry point' for your application (usually the main view or main method).

The container should take care of wiring up and creating the dependencies for everything that stems from that composition root.

Example:

(Pseudo code)

public class ApplicationBootstrapper
{
private IContainer _container;

public ApplicationBootstrapper() {
_container = new SomeDIContainer();

_container.Register<SomeComponent>().AsSingleton(); // Singleton instance, same instance for every resolve
_container.Register<SomeOtherComponent>().AsTransient(); // New instance per resolve
// ... more registration code for all your components
// most containers have a convention based registration
// system e.g. _container.Register().Classes().BasedOn<ViewModelBase> etc

var appRoot = _container.Resolve<MainWindowViewModel>();
appRoot.ShowWindow();
}
}

Now when your application runs, all dependencies are injected into the root and all dependencies of the root and so on

Your MainWindowViewModel could then specify a dependency on the UC as such:

public MainWindowViewModel(UC1ViewModel vm)
{
}

Notice how the MainWindowViewModel no longer needs an ILocalizer instance, it will be resolved and injected into the UC1ViewModel for you (unless of course you need it).

Couple of points to note

  • You should not pass an instance of the container around. If you are referencing the container in your application code anywhere other than during application startup you are probably doing something wrong

  • Deferred resolution of dependencies is usually achieved with factories (types that are designed specifically to resolve from the container on behalf of your components). The factory should be injected into the component and the component can then call the factory to get the instance it needs. This also allows you to pass arguments to the dependency.

  • Use SOLID principles, depend on abstractions not concrete classes. This way it's much easier to swap out components if you decide to change the way something works (you just change the registration code to use a different concrete class that implements the same interface, et voila, no refactoring the app)

Anything else

This is by no means a concise view of DI, there is a lot to consider, but hopefully it will get you started. As Steven mentioned, if you are planning on redistributing the library you should read up on best practices.

The original post on dos/dont's is here:

Dependency Inject (DI) "friendly" library

Which DI container should you use?

The world is your oyster. I'm a fan of Castle Windsor - it's not the fastest (I can't think of an app I've written where I've ever needed component resolution to be ninja fast...), but it's certainly fully featured.

Update: couple of non queries I didn't really address

Plugins

Castle Windsor has plugin capabilities built in - so you can drop a DLL into your application directory which adds functionality to your application by registering components with the container. Not sure if this applies to your UC class library or not (you could just make the app depend on it unless it needs to actually be a plugin)

Other stuff

There are also quite a lot of MVVM frameworks with several different approaches on view/viewmodel resolution (viewmodel-first, view-first, hybrid approaches).

You may want to consider using one of these to help guide you in structuring your application if you are not already using one (it doesn't sound like you are).

.NET core dependency injection vs static

Version with DI is better. There are some reasons:

  1. You can inject different implementations without any need to change your controller code
  2. It's less difficult to test code with DI (with static classes it's often impossible)
  3. Static classes are mostly global public. Stateful static classes could act like global variables in other languages that can produce unpredictable issues
  4. It's easy to guess dependencies of controller by checking its constructor
  5. Also it's less difficult to control db connection using DI (you can use repository and unit of work patterns for example, open connection to db only when it's needed)
  6. With DI it's easy to extend behavior of your method. For example, you might need to return data from local cache instead of db. With DI it could be done using decorator for your service, while with static class you will have to modify this class itself even if you need caching only in single place of your code

Also I wanna add that using static classes everywhere is not OOP approach. So I recommend to use instances of you classes and DI instead

Is there a way to use static method with dependency injection in NestJS?

Static methods cannot use dependency injection. This is because the idea of dependency injection (at least to Nest) is to inject instances of the dependencies so that they can be leveraged later.

The code you have is valid, in that it will return the value 1 like the static method says to, but the static method cannot use any of the instance values that are injected. You'll find this kind of logic follows in most other DI frameworks.



Related Topics



Leave a reply



Submit