Dependency Inversion Principle in C#

Dependency Inversion Principle in C# with Examples

In this article, I am going to discuss the Dependency Inversion Principle in C# with Examples. Please read our previous article before proceeding to this article where we discussed the Interface Segregation Principle in C# with a real-time example. The Letter D in SOLID stands for the Dependency Inversion Principle which is also known as DIP. At the end of this article, you will understand the following pointers in detail.

Dependency Inversion Principle in C# with Examples

What is the Dependency Inversion Principle in C#?

The Dependency Inversion Principle (DIP) states that high-level modules/classes should not depend on low-level modules/classes. Both should depend upon abstractions. Secondly, abstractions should not depend upon details. Details should depend upon abstractions.

The most important point that you need to remember while developing real-time applications, always to try to keep the High-level module and Low-level module as loosely coupled as possible.

When a class knows about the design and implementation of another class, it raises the risk that if we do any changes to one class will break the other class. So we must keep these high-level and low-level modules/classes loosely coupled as much as possible. To do that, we need to make both of them dependent on abstractions instead of knowing each other.

Let us understand the Dependency Inversion Principle in C# with one example

Let’s create one console application, Then create the following classes

Employee.cs

Create a class file with the name Employee.cs and then copy and paste the following code into it. The following is a simple class having 4 properties.

namespace SOLID_PRINCIPLES.DIP
{
    public class Employee
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Department { get; set; }
        public int Salary { get; set; }
    }
}
EmployeeBusinessLogic.cs

Create a class file with the name EmployeeBusinessLogic.cs and then copy and paste the following code into it. The following class has one constructor that is used to create an instance of EmployeeDataAccess. Here, within the constructor we call the static GetEmployeeDataAccessObj() method on the DataAccessFactory class which will return an instance of EmployeeDataAccess and we initialize the _EmployeeDataAccess property with the return instance. We have also one method i.e. GetEmployeeDetails which is used to call the GetEmployeeDetails method on the EmployeeDataAccess instance to get the employee detail by employee id.

namespace SOLID_PRINCIPLES.DIP
{
    public class EmployeeBusinessLogic
    {
        EmployeeDataAccess _EmployeeDataAccess;
        public EmployeeBusinessLogic()
        {
            _EmployeeDataAccess = DataAccessFactory.GetEmployeeDataAccessObj();
        }
        public Employee GetEmployeeDetails(int id)
        {
            return _EmployeeDataAccess.GetEmployeeDetails(id);
        }
    }
}
DataAccessFactory.cs

Create a class file with the name DataAccessFactory.cs and then copy and paste the following code into it. The following class contains one static method which is returning an instance of the EmployeeDataAccess class.

namespace SOLID_PRINCIPLES.DIP
{
    public class DataAccessFactory
    {
        public static EmployeeDataAccess GetEmployeeDataAccessObj()
        {
            return new EmployeeDataAccess();
        }
    }
}
EmployeeDataAccessLogic.cs

Create a class file with the name EmployeeDataAccess.cs and then copy and paste the following code into it. The following class contains one method which takes the employee id and returns that Employee information.

namespace SOLID_PRINCIPLES.DIP 
{
    public class EmployeeDataAccess
    {
        public Employee GetEmployeeDetails(int id)
        {
            // In real time get the employee details from db
            //but here we are hard coded the employee details
            Employee emp = new Employee()
            {
                ID = id,
                Name = "Pranaya",
                Department = "IT",
                Salary = 10000
            };
            return emp;
        }
    }
}
Let us compare the above example with the Dependency Inversion Principle in C#

As per the Dependency Inversion Principle definition“a high-level module should not depend on low-level modules. Both should depend on the abstraction”.

So, first, we need to figure out which one is the high-level module (class) and which one is the low-level module (class) in our example. A high-level module is a module that always depends on other modules. So, in our example, the EmployeeBusinessLogic class depends on EmployeeDataAccess class, so here the EmployeeBusinessLogic class is the high-level module and the EmployeeDataAccess class is the low-level module.

So, as per the first rule of the Dependency Inversion Principle in C#, the EmployeeBusinessLogic class/module should not depend on the concrete EmployeeDataAccess class/module, instead, both the classes should depend on abstraction.

The second rule of the Dependency Inversion Principle state that “Abstractions should not depend on details. Details should depend on abstractions”.

Before understanding this let us first understand what is an abstraction.

What is Abstraction?

In simple words, we can say that Abstraction means something which is non-concrete. So, abstraction in programming means we need to create either an interface or abstract class which is non-concrete so that we can not create an instance of it. In our example, the EmployeeBusinessLogic and EmployeeDataAccess are concrete classes that mean we can create objects of it.

As per the Dependency Inversion Principle in C#, the EmployeeBusinessLogic (high-level module) should not depend on the concrete EmployeeDataAccess (low-level module) class. Both classes should depend on abstractions, meaning both classes should depend on either an interface or an abstract class.

What should be in the interface (or in the abstract class)?

As you can see in the above example, the EmployeeBusinessLogic uses the GetEmployeeDetails() method of EmployeeDataAccess class. In real-time, there will be many employee-related methods in the EmployeeDataAccess class. So, we need to declare the GetEmployeeDetails(int id) method within the interface. Add one interface with the name IDataAccess and then copy and paste the following codes.

IDataAccess.cs

Create a class file with the name IEmployeeDataAccess.cs and then copy and paste the following code into it. As you can see, here we created the interface with one abstract method i.e. GetEmployeeDetails.

namespace SOLID_PRINCIPLES.DIP
{
    public interface IEmployeeDataAccess
    {
        Employee GetEmployeeDetails(int id);
    }
}

Now, we need to implement the IEmployeeDataAccess in EmployeeDataAccess class. So, modify the EmployeeDataAccess class as shown below

EmployeeDataAccess.cs
namespace SOLID_PRINCIPLES.DIP
{
    public class EmployeeDataAccess : IEmployeeDataAccess
    {
        public Employee GetEmployeeDetails(int id)
        {
            // In real time get the employee details from db
            //but here we are hardcoded the employee details
            Employee emp = new Employee()
            {
                ID = id,
                Name = "Pranaya",
                Department = "IT",
                Salary = 10000
            };
            return emp;
        }
    }
}

Now, we need to change the factory class which will return the IEmployeeDataAccess instead of the concrete EmployeeDataAccess class as shown below.

DataAccessFactory.cs

Modify the DataAccessFactory class below. Please notice here we have changed the return type of the GetEmployeeDataAccessObj method from EmployeeDataAccess to IEmployeeDataAccess.

namespace SOLID_PRINCIPLES.DIP
{
    public class DataAccessFactory
    {
        public static IEmployeeDataAccess GetEmployeeDataAccessObj()
        {
            return new EmployeeDataAccess();
        }
    }
}

Now, we need to change the EmployeeBusinessLogic class which will use the IEmployeeDataAccess instead of the concrete EmployeeDataAccess class as shown below.

namespace SOLID_PRINCIPLES.DIP
{
    public class EmployeeBusinessLogic
    {
        IEmployeeDataAccess _EmployeeDataAccess;
        public EmployeeBusinessLogic()
        {
            _EmployeeDataAccess = DataAccessFactory.GetEmployeeDataAccessObj();
        }
        public Employee GetEmployeeDetails(int id)
        {
            return _EmployeeDataAccess.GetEmployeeDetails(id);
        }
    }
}

That’s it. We have implemented the Dependency Inversion Principle in our example where the high-level module (EmployeeBusinessLogic) and low-level module (EmployeeDataAccess) depend on abstraction (IEmployeeDataAccess). Also, abstraction (IEmployeeDataAccess) does not depend on details (EmployeeDataAccess) but details depend on abstraction.

Advantages of implementing the Dependency Inversion Principle in C#:

Now, the EmployeeBusinessLogic and EmployeeDataAccess classes are loosely coupled classes because EmployeeBusinessLogic does not depend on concrete EmployeeDataAccess class, instead, it includes a reference of IEmployeeDataAccess interface. So now, we can easily use another class that implements IEmployeeDataAccess with a different implementation.

In this article, I try to explain the Dependency Inversion Principle in C# with Examples. I hope this article will help you with your needs. I would like to have your feedback. Please post your feedback, question, or comments about this Dependency Inversion Principle in C# with Examples article.

21 thoughts on “Dependency Inversion Principle in C#”

  1. blank

    I am very happy for finding this great site. I couldn’t skip writing a comment to thank you, keep the great work up!

Leave a Reply

Your email address will not be published. Required fields are marked *