Dependency Injection
_______________________________________________________
ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies.
In software engineering, dependency injection is a technique in which an object receives other objects that it depends on. These other objects are called dependencies. In the typical "using" relationship the receiving object is called a client and the passed (that is, "injected") object is called a service
Dependency Inversion and Dependency Injection
Dependency Inversion is one of the core software design principles which we use to create modules that are loosely coupled. While creating applications, we can achieve this using a technique called Dependency Injection. Dependency Injection (DI) is the method of injecting the dependent modules into our classes.
We have discussed this in detail in one of our other article Dependency Inversion Principle. There, we discussed the concept of Dependency Injection and how to implement it.
So, in this section, we are going to look at the support for Dependency Injection in an ASP.NET Core MVC application.
Injecting Dependencies into Controllers
ASP.NET Core supports Dependency Injection(DI) between classes and their dependencies. MVC Controllers request dependencies explicitly via constructors. Furthermore, ASP.NET Core has built-in support for dependency injection, hence making the application easier to test and maintain.
We add services as a constructor parameter and the runtime resolves the service from the service container. We typically define services using interfaces.
When we implement a repository pattern in the ASP.NET Core MVC application, we make use of Dependency Injection in our controllers. We have explained how to implement a simple data repository in the article section Implementing a simple data repository.
Let’s create an ASP.NET Core MVC application and implement a simple data repository as described in the article.
First of all, let’s create an interface IDataRepository:
public interface IDataRepository<TEntity>
{
IEnumerable<TEntity> GetAll();
void Add(Employee employee)
}
Then let’s create a class EmployeeManager implementing the IDataRepository interface:
public class EmployeeManager : IDataRepository<Employee>
{
public void Add(Employee employee)
{
throw new NotImplementedException();
}
IEnumerable<Employee> IDataRepository<Employee>.GetAll()
{
return new List<Employee>() {
new Employee(){ }
};
}
}
Next step is to add the service to the service container. We need to do that in the ConfigureServices() method in the Startup.cs class:
services.AddScoped<IDataRepository<Employee>, EmployeeManager>();
By doing so, we have configured the repository using Dependency Injection.
Next, let’s create the EmployeeController with the Index() action method to get the list of all employees:
public class EmployeeController : Controller
{
private readonly IDataRepository<Employee> _dataRepository;
public EmployeeController(IDataRepository<Employee> dataRepository)
{
_dataRepository = dataRepository;
}
public IActionResult Index()
{
IEnumerable<Employee> employees = _dataRepository.GetAll();
return View(employees);
}
}
public IActionResult Index([FromServices] IDataRepository<Employee> _dataRepository)
{
IEnumerable<Employee> employees = _dataRepository.GetAll();
return View(employees);
}
Here, we first declare a _dataRepository variable of type IDataRepository<Employee>. Later, we inject it through the constructor.
We can also inject a service directly into an action method without using a constructor injection. We can use the [FromServices] attribute for that:
public IActionResult Index([FromServices] IDataRepository<Employee> _dataRepository)
{
IEnumerable<Employee> employees = _dataRepository.GetAll();
return View(employees);
}
TheFromServices attribute specifies that an action parameter should be bound using the request services.
Great. We have learned how to use Dependency Injection to provide dependencies into a Controller.
Now, let’s look at how to Inject dependencies into Views.
Injecting Dependencies into Views
ASP.NET Core supports injecting dependencies into Views.
When to Inject Dependencies into Views
Injecting services into views is a deviation from the MVC concept. But in some cases, we may have view-specific services which return the data that we use only for populating the view elements. An example is a service that gives a set of values that we need to display in a list control. In such scenarios, we can inject services directly into views. View injection can be useful to populate options in UI elements, such as dropdown lists.
Consider a form to create a book that includes options for specifying a genre. Rendering the data using a standard MVC approach would require the controller to request data access services for this set of options and then populate a Model or ViewBag with the set of options to be bound.
An alternative approach is to inject the services directly into a view. This approach minimizes the amount of code required by the controller as we move the view element construction logic into the view itself.
How to Inject services into Views
We can inject a service into a view using the @inject directive. @inject adds a property to our view and initialize it using DI:
@inject <type> <name>
public class BooksController : Controller
{
public IActionResult Create()
{
return View();
}
}
Then let’s create a Book
model class:
public class Book
{
public int Id { get; set; }
[Display(Name = "Book Title")]
public string Title { get; set; }
public string Genre { get; set; }
[DataType(DataType.Currency)]
[Range(1, 100)]
public decimal Price { get; set; }
[Display(Name = "Publish Date")]
[DataType(DataType.Date)]
public DateTime PublishDate { get; set; }
}
For the next step, let’s define a BooksLookupService for supplying the list data for Genres:
public class BooksLookupService
{
public List<string> GetGenres()
{
return new List<string>()
{
"Fiction",
"Thriller",
"Comedy",
"Autobiography"
};
}
}
Then let’s create a view and inject an instance of BooksLookupService into it:
@model WorkingWithDI.Models.Book
@inject WorkingWithDI.Models.Services.BooksLookupService BooksLookupService
@{
ViewData["Title"] = "Create";
var genres = BooksLookupService.GetGenres();
}
<h1>Create</h1>
<h4>Book</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div>
<label asp-for="Genre" class="control-label"></label>
<select asp-items="@(new SelectList(genres))" class="form-control" ></select>
</div>
<div class="form-group">
<label asp-for="Price" class="control-label"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PublishDate" class="control-label"></label>
<input asp-for="PublishDate" class="form-control" />
<span asp-validation-for="PublishDate" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
This will supply the list values into the view.
As the last step, we need to register the types that we request through dependency injection in Startup.ConfigureServices(). If a type is unregistered, it throws a runtime exception.
services.AddTransient<BooksLookupService>();
That’s it. Now let’s run the application and navigate to Create Books form:
We can see that the list of Genres is populated by getting the values from the BooksLookupService.
Excellent, we have learned how to inject a dependency directly into the view.
_____________________________