The dependency injection framework that is built into the modern .NET works well enough to satisfy the needs of most projects. However, it's not as feature-rich as the most popular 3rd party libraries. One of the features that was missing before .NET 8 was the ability to register multiple implementations of the same interface and have the ability to specify which one should be injected.
Posts about Dependency Injection
In large projects, service registration code for dependency injection can grow rather large. Forgetting to register a newly added dependency will break your application at the point of instantiating a class (transitively) requiring it. It's even more likely that this will happen to you if you need to register a dependency in multiple places (e.g., for multiple projects) and don't have tests for every one of them.
By default, services in Angular are treated as singletons – the same instance is used throughout the application. If this is not the desired behavior, you can change it by removing the providedIn property from the Injectable decorator for the service and then add the service as a provider to each component.
The new console application template in .NET 6 is as minimal as possible thanks to top-level statements. That's great for simple applications, but what if you want to create a large console application with a sophisticated command-line interface?
Angular's dependency injection makes it really easy to inject services into components and other services. In most cases, that's all we need. But what if we need more than one implementation of the same service contract? To accomplish this in strongly typed languages, we typically create interfaces as contracts that can be implemented by multiple services. When an instance of the interface is requested, the correct implementation is injected based on the dependency injection configuration. In Angular, this does not work.
By default, services in Angular are provided at the root module level. This way, the same instance of the service will be injected into any component depending on it. If a component needs a separate instance of the service for itself and its children, it can change the scope by declaring a service provider itself. However, this change also affects dependency injection in tests.
A while ago I've already written a blogpost on how to inject a different Angular service implementation based on a runtime value. With that approach, the selected service was initialized at startup and remained the same for the entire application lifetime. In response to that blogpost, I received a question how one could switch between the implementations while the application is running. This blogpost is my detailed answer to that question.
I keep getting impressed by how feature-rich dependency injection in Angular is. This time I needed it to inject the appropriate implementation of a dependency based on runtime information. Of course, the scenario is well supported.
Angular has an impressive dependency injection system, however some aspects could be documented better. Old blog posts explaining how things worked before the final release don't help either. Hence, it took me a while to successfully intercept HTTP requests and inject a common parameter.
The general advice is to avoid using IoC containers in your test code altogether. Unfortunately achieving that when IoC usage is being retrofitted into an existing application, can be challenging. Failing to do that will quickly result in having to reconfigure your IoC container for some of the tests.
One of the main reasons for using an IoC container like Ninject is to be able to inject different dependencies into your classes in production code and in test code. Usually you don't even need to use an IoC container in your tests, but when you longer lived dependencies will usually be scoped differently in test code than they are in production code.
Most of the time there's no need to use an IoC container in unit tests. Still, testing the IoC container configuration makes sense in an application to avoid runtime errors which will occur when not all required dependencies for the created object are registered in the application beforehand. This doesn't necessarily mean, the right implementations are being used for all dependencies, but that's not what we want to test.