Introduction

Before understanding Dependency Injection, you should be familiar with the two concepts of Object Oriented Programming, Tight Coupling and Loose Coupling. So, let's see each, one by one.

Tight Coupling

When a class is dependent on a concrete dependency, it is said to be tightly coupled to that class. A tightly coupled object is dependent on another object; that means changing one object in a tightly coupled application often requires changes to a number of other objects. It is not difficult when an application is small but in an enterprise level application, it is too difficult to make the changes.

Loose Coupling

It means two objects are independent and an object can use another object without being dependent on it. It is a design goal that seeks to reduce the inter- dependencies among components of a system with the goal of reducing the risk that changes in one component will require changes in any other component.

Now, in short, Dependency Injection is a pattern that makes objects loosely coupled instead of tightly coupled. Generally, we create a concrete class object in the class where we require the object, and bind it in the dependent class. But, DI is a pattern where we create a concrete class object outside this high-level module or dependent class.

There are three types of Dependency Injections:

  1. Constructor Dependency Injection
  2. Setter Dependency Injection
  3. Interface Dependency Injection

Dependency Injection (DI) Container

The Dependency Injection Container is a framework to create dependencies and inject them automatically when required. It automatically creates objects based on requests, and injects them when required. It helps us split our application into a collection of loosely-coupled, highly-cohesive pieces, and then, glue them back together in a flexible manner. By DI container, our code will become easier to write, reuse, test, and modify. In this article, we will use a Ninject DI Container.

Using the Code

We create a sample console application in C#, using Visual Studio 2015. This application has the following features.

  • The Ninject IoC is used to implement Dependency Injection.
  • Creating a generic repository for inserting the collection of entities in a database.
  • Read data from JSON file and deserialize JSON data to entity/entities.
  • Database first approach is used to perform the insert operation.
  • With Dispose pattern.

First of all, we create a table Employee in the database, using the following script.

Create Table Employee 
EmployeeId bigint identity(1,1) primary key
FirstName nvarchar(50), 
LastName nvarchar(50), 
Email nvarchar(100) 
)

We create four projects in the solution, as follow.

  • DIConsole: A console application which runs. It has integration of IoC(Ninject).
  • DI.Data: It's a class library which has an edmx file where the database table is mapped.
  • DI.Repo: It's a class library that performs insert operation for the entity.
  • DI.Service: It's a class library that communicates to console application by the interface.

The communication flow among these projects is shown in the following figure.

Figure 1: Operation work flow

We install Entity Framework using NuGet package, as shown in following figure 2.

Figure 2: Install Entity Framework by NuGet package

We create edmx file and map database's tables in it.

Figure 3: Entity in edmx file

Now, we create IRepository interface in DI.Repo project. It has two method signatures- First one is InsertCollection which is used to insert Entity Collection and another one is the Dispose method signature. The following is the code snippet for the same.

using System.Collections.Generic; 
   
namespace DI.Repo 
    public interface IRepository<TEntity> where TEntity : class 
    
        void InsertCollection(List<TEntity> entityCollection); 
        void Dispose(); 
    
}

After that, we create Repository class in the same project that has an implementation of IRepository interface, as per the following code snippet.

using DI.Data; 
using System; 
using System.Collections.Generic; 
using System.Data.Entity; 
using System.Data.Entity.Validation; 
using System.Text; 
   
namespace DI.Repo 
    public class Repository<TEntity> : IRepository<TEntity> where TEntity : class 
    
        internal DIConsoleEntities context; 
        internal IDbSet<TEntity> dbSet; 
   
        public Repository(DIConsoleEntities context) 
        
            this.context = context; 
            this.dbSet = context.Set<TEntity>(); 
        
   
        public void InsertCollection(List<TEntity> entityCollection) 
        
            try 
            
                entityCollection.ForEach(e => 
                
                    dbSet.Add(e); 
                });                
                context.SaveChanges(); 
            
            catch (DbEntityValidationException ex) 
            
                StringBuilder sb = new StringBuilder(); 
   
                foreach (var failure in ex.EntityValidationErrors) 
                
                    sb.AppendFormat("{0} failed validation\n", failure.Entry.Entity.GetType()); 
                    foreach (var error in failure.ValidationErrors) 
                    
                        sb.AppendFormat("- {0} : {1}", error.PropertyName, error.ErrorMessage); 
                        sb.AppendLine(); 
                    
                
   
                throw new DbEntityValidationException( 
                    "Entity Validation Failed - errors follow:\n"
                    sb.ToString(), ex 
                ); 
            
        
   
        public void Dispose() 
        
            GC.SuppressFinalize(this); 
        
    
}

We create IEmployeeService in DI.Service project which communicates to the console application. The following is the code snippet for the same.

using DI.Data; 
using System; 
using System.Collections.Generic; 
   
namespace DI.Service 
    public interface IEmployeeService : IDisposable 
    
        List<Employee> InsertEmployee(List<Employee> employees); 
    
}

We implement the above interface in EmployeeService class in the same project, as shown in following code snippet.

using System.Collections.Generic; 
using DI.Data; 
using DI.Repo; 
   
namespace DI.Service 
    public class EmployeeService : IEmployeeService 
    
        private readonly IRepository<Employee> repoEmployee; 
   
        public EmployeeService(IRepository<Employee> repoEmployee) 
        
            this.repoEmployee = repoEmployee; 
        
   
        public List<Employee> InsertEmployee(List<Employee> employees) 
        
            repoEmployee.InsertCollection(employees); 
            return employees; 
        
   
        private bool disposedValue = false
   
        protected virtual void Dispose(bool disposing) 
        
            if (!disposedValue) 
            
                if (disposing) 
                
                    repoEmployee.Dispose(); 
                
                disposedValue = true
            
        
   
        public void Dispose() 
        
            Dispose(true); 
        
    
}

Now, we work in a DIConsole project in which we install Ninject by a NuGet package, using Package Manager Console window. We run the following command to install it.

PM> Install-Package Ninject -Version 3.2.2

After that, we install Newtonsoft JSON by a NuGet package, using Package Manager Console window. We run the following command to install it.

PM>Install-Package Newtonsoft.JSON

We register repository and context class in Ninject module which helps in creating the instance of these classes. The following class is added in the DIConsole project.

using DI.Repo; 
using Ninject.Modules; 
using System; 
   
namespace DIConsole 
    public class EmployeeExportModule:NinjectModule 
    
        public override void Load() 
        
            Bind(Type.GetType("DI.Data.DIConsoleEntities, DI.Data")).ToSelf().InSingletonScope(); 
            Bind(typeof(IRepository<>)).To(typeof(Repository<>)).InSingletonScope(); 
        
    
}

We create an operation class which calls insert method of employee service. Here, we use IEmployeeService instance created by Dependency Injection. This class has a LoadEmployeeJson method which reads data from JSON file and deserialize the data in the Employee collection.

using DI.Data; 
using DI.Service; 
using Newtonsoft.Json; 
using Ninject; 
using System; 
using System.Collections.Generic; 
using System.IO; 
   
namespace DIConsole 
    public class EmployeeExportOperation :IDisposable 
    
        private readonly IEmployeeService employeeService; 
   
        public EmployeeExportOperation(IKernel kernel) 
        
            employeeService = kernel.Get<EmployeeService>(); 
        
   
        public void SaveEmployee() 
        
            List<Employee> employees = LoadEmployeeJson(); 
            employeeService.InsertEmployee(employees); 
        
        private List<Employee> LoadEmployeeJson() 
        
            using (StreamReader streamReader = new StreamReader("employee.json")) 
            
                string json = streamReader.ReadToEnd(); 
                List<Employee> employees = JsonConvert.DeserializeObject<List<Employee>>(json); 
                return employees; 
            
        
        private bool disposedValue = false;  
   
        protected virtual void Dispose(bool disposing) 
        
            if (!disposedValue) 
            
                if (disposing) 
                
                    employeeService.Dispose(); 
                
                disposedValue = true
            
        
   
        public void Dispose() 
        {             
            Dispose(true);            
        }  
    
}

Now, we update the Main method of the program class to call the saveEmployee method of EmployeeExportOpertion, as per the following code snippet.

using Ninject; 
   
namespace DIConsole 
    class Program 
    
        static void Main(string[] args) 
        
            IKernel kernel = new StandardKernel(new EmployeeExportModule()); 
            EmployeeExportOperation employeeExportOperation = new EmployeeExportOperation(kernel); 
            employeeExportOperation.SaveEmployee(); 
            System.Console.ReadKey(); 
        
    
}

Now, run the application and the result displays as per the following figure.

Figure 4: Result of application

Download

You can download the complete solution source code from this https://code.msdn.microsoft.com/Dependency-Injection-in-e7adb082

See Also

There are some more article related C#. These are:

  1. C# 7 : Func, Action And Local Function
  2. C# 7 : Tuple
  3. C# : Why Use Property