In this blog post, I give a brief overview of what dependency injection is and its benefits. Furthermore, I will go into some detail on how dependency injection can be used in Acumatica.
What is Dependency Injection?
As you can possibly infer from the word dependency in the term “dependency injection”, it indicates that some parts of the code might have a dependency or rely on other parts of your code. In any given codebase there can be two types of dependencies, either loosely or strongly coupled. In the case of loosely coupling, the code would depend on an abstract class or interface whilst in strongly coupled code, it actually depends on a concrete class.
GIST: https://gist.github.com/coqmo5/d7f99301e2fbd3a7c1de4c0b4d7d05cf
Therefore, as one might assume now, dependency injection is at its core a set of design principles and patterns that enable the developer to create loosely coupled code. These design patterns dictate the way of providing or “injecting” the dependencies in our code.
Why should you use Dependency Injection?
There are many advantages of using dependency injection and similar patterns. Some benefits are:
- Reducing responsibility: In dependency injection, the responsibility shifts from the class making the call to the one that is being called. Taking the above example into consideration the ValidateAndMultiply function does not care how the validator object has been provided or by whom. Its only responsibility is to call the function, and the fewer responsibilities a piece of code has, the less error-prone it will be.
- Code re-usability: When you use dependency injection, the object initialization is not done for every instance but is managed by the DI container. It will then be available to all the code parts that require it, just by passing the initialized object.
- No need to modify unrelated code: Should the validator class require some modifications in the future, there will be no need to modify the MainCaller class and other classes where there are validator references. This will also be easier to test when the code is modified.
Dependency Injection in Acumatica
Dependency injection in Acumatica is quite straightforward, as there is no need to create a DI container. This is because the mapping between the interface and its implementation is done by the graph in itself upon creation. The graph is also responsible for the initialization and lifetime of our objects. Therefore, all that is required from our side is to define the contracts (interfaces) and their respective implementations.
This can be quite beneficial as you can have a separate class library with these contracts and their implementations that are independent of a single project which is then referenced by the project’s extension library.
In order to use dependency injection in Acumatica, we must first define the contract that will be used to inject their respective implementation in our graphs.
GIST: https://gist.github.com/coqmo5/cfbf1c557a63241e0bf9bcbf0a2237ef
Once the contract is defined, the next step is to implement it. The implementation class will contain the logic that will be executed when the interface function signature is called from our graphs.
GIST: https://gist.github.com/coqmo5/de86a958c52864377dbbafc441e423f4
When both the contract and its implementation have been defined, the last step is to help the framework map to the correct implementation. As any given interface can have more than a single implementation class. In order to do this, we would need to register our contracts and implementations with Autofac by defining a module for our project extension library.
GIST: https://gist.github.com/coqmo5/7b7c17b91ef75042cd8c8c64d9b0392f
Once all this is completed, we can freely use our implementation in our graph and graph extensions. In order to do so, we must first define a property of our interface decorated by the [InjectDependency] attribute and then actually call the method signature from that interface.
GIST: https://gist.github.com/coqmo5/755aff8d47fa316ab8e3ebaac3e62c1b
Conclusion
At first glance, it may seem that using dependency injection is a roundabout way of doing things. However, through its use, we can reduce a significant amount of code in our applications. This is due to the fact that a big part of the code can be reused between different projects, whilst also minimally impacting the effect it has on an already existing code base, especially when addressing changes in the implementation classes.
Hope you have found this post useful – Happy Coding!