top button
Flag Notify
    Connect to us
      Site Registration

Site Registration

What is Dependency Injection?

+4 votes
285 views

What is Dependency Injection?

Dependency Injection (DI) is a software design pattern that deals with how components get hold of their dependencies.

The Angular injector subsystem is in charge of creating components, resolving their dependencies, and providing them to other components as requested.

DI is pervasive throughout Angular. You can use it when defining components or when providing run and config blocks for a module.

Components such as services, directives, filters, and animations are defined by an injectable factory method or constructor function. These components can be injected with "service" and "value" components as dependencies.

Controllers are defined by a constructor function, which can be injected with any of the "service" and "value" components as dependencies, but they can also be provided with special dependencies. See Controllers below for a list of these special dependencies.

The run method accepts a function, which can be injected with "service", "value" and "constant" components as dependencies. Note that you cannot inject "providers" into run blocks.

The config method accepts a function, which can be injected with "provider" and "constant" components as dependencies. Note that you cannot inject "service" or "value" components into configuration.

Video for Dependency Injection

posted Nov 26, 2015 by anonymous

  Promote This Article
Facebook Share Button Twitter Share Button LinkedIn Share Button


Related Articles

Dependency Injection (DI) is a technique to develop loosely coupled software systems. Although this article won't go into the details of DI as such, the basic idea is this:

Assume that there is a class Class1 that depends on Class2 and Class2 for its functioning. The normal practice is to instantiate the objects of Class2 and Class3 inside Class1 as shown below:

public class Class1
{
   private Class2 objClass2;
   private Class3 objClass3;

    public Class1()
    {
      objClass2 = new Class2();
      objClass3 = new Class3();
    }
   ....
   ....
}

Although this might work as expected it has a problem of its own - there is tight coupling between Class1 and its dependencies (Class2 and Class3). If you wish to replace Class2 and Class3 with some other implementations you need to change the code of Class1 since these objects are created inside the class.

To get away from this dependency you can do the following:

  • Base Class2 and Class3 on some interface.
  • Supply objects of classes implementing the interface (Class2 and Class3 in this case) from external world.

In other words you inject dependencies of a class from the external world. So, Class1 will look like this:

public class Class1
{
    public Class1(ISomeInterface objClass2, 
                  ISomeOtherInterface objClass3)
    {
      ....
    }
}

Ok. Now that you have some idea about DI, let's see how ASP.NET Core 1.0 allows you to work with it.

As far as ASP.NET Core 1.0 is concerned, a type to be injected is called a service. The ASP.NET Core dependency injection framework does two basic tasks for you:

  • It instantiates an object of the said service and supplies it to controllers. The dependencies can be injected through constructor injection or through property injection.
  • It handles the lifetime (when to create an object and when to dispose it) of the injected object.

There are three lifetime modes for a service being injected:

  • Singleton : An object of a service is created and supplied to all the requests to that service. So, basically all requests get the same object to work with.
  • Scoped : An object of a service is created for each and every request. So, each request gets its a new instance of a service to work with.
  • Transient : An object of a service is created every time an object is requested.

Let's see how each of these modes work.

Begin by creating a new ASP.NET Core 1.0 project as outlined. Then add an interface and a class to the Classes (or some other folder of you choice) folder:

public interface IMyServiceInterface
{
    string UniqueID { get; set; }
}

public class MyService:IMyServiceInterface
{
    public string UniqueID { get; set; }

    public MyService()
    {
        this.UniqueID = Guid.NewGuid().ToString();
    }
}

The MyServiceInterface class defines a single property - UniqueID. The MyService class implements IMyServiceInterface. The UniqueID property is set to a new GUID in the constructor of the MyService class. This way you can observe the value of UniqueID to understand the lifetime modes mentioned above.

Then add HomeController and Index view to the project.

Singleton

The DI services and their lifetime modes are registered in the Startup class. So, open the Startup class and modify the ConfigureServices() method as shown below:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddSingleton<IMyServiceInterface,MyService>();
}

Notice the line marked in bold letters. It uses the AddSingleton() method of the IServiceCollection to register MyService class as a service type. As the name suggests AddSingleton() method registers MyService with singleton mode.

Next, open the HomeController and write the following code to it:

public class HomeController : Controller
{
    private IMyServiceInterface obj;

    public HomeController(IMyServiceInterface obj)
    {
        this.obj = obj;
    }

    public IActionResult Index()
    {
        ViewBag.Obj = this.obj;
        return View();
    }
}

The HomeController constructor takes a parameter of IMyServiceInterface. Since a type that implements this interface is registered with the DI framework, an object of MyService is created and supplied to the constructor. The object reference is stored in a local obj variable for later use.

The Index() action, simply sets a ViewBag property to this injected object. The Index view outputs the value of UniqueID property as follows:

<h4>UniqueID of Obj : @ViewBag.Obj.UniqueID</h4>

If you run the application your browser should look something like this:

image

Now open another browser tab and issue another request to /home/index. You will find that both the tabs output the same UniqueID confirming that a singleton object is being created.

Scoped

To change the lifetime mode to Scoped, open Startup class again and change the call as shown below:

 services.AddScoped<IMyServiceInterface,MyService>();

This time the code uses AddScoped() method. After making this change run the application again. Simulate multiple requests as before. This time you will find that each tab displays different UniqueID. This conforms that each request is being supplied with a new object instance.

image

Transient

To test the transient mode you need to change the Startup class like this:

services.AddTransient<IMyServiceInterface,MyService>();

This time the code uses AddTransient() method to register MyService. Now, to simulate multiple object creation requests within a single request-response cycle modify HomeController as shown below:

public class HomeController : Controller
{
    private IMyServiceInterface obj1;
    private IMyServiceInterface obj2;

    public HomeController(IMyServiceInterface obj1, 
                   IMyServiceInterface obj2)
    {
        this.obj1 = obj1;
        this.obj2 = obj2;
    }

    public IActionResult Index()
    {
        ViewBag.Obj1 = this.obj1;
        ViewBag.Obj2 = this.obj2;
        return View();
    }
}

This time the HomeController declares two variables of IMyServiceInterface. The constructor accepts two parameters of IMyServiceInterface - obj1 and obj2. This is just to simulate multiple object creation requests. The obj1 and obj2 are stored in ViewBag as before.

The Index view outputs the UniqueID from both the objects like this:

<h4>UniqueID of Obj : @ViewBag.Obj1.UniqueID</h4>
<h4>UniqueID of Obj2 : @ViewBag.Obj2.UniqueID</h4>

To test the transient mode, run the application. You will find that the two UniqueID values are different even for a single HTTP request.

image

Special case of Singleton - instance

You can register a singleton service by creating its instance yourself. In this case you are responsible for creating an object of a service. The DI framework then uses that instance in singleton mode mentioned earlier. For example, you can create an instance of MyService and register it with the DI framework. You can do so in ConfigureServices() as shown below:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    MyService obj = new MyService();
    services.AddSingleton<IMyServiceInterface>(obj);
}

As you can see, the code creates an object of MyService instead of relying on DI framework. This manually created object instance is then registered with the DI framework using an overload of AddSingleton() method. Notice that this overload accepts an object instance that is then registered as a singleton instance with the system.

You can run the application as before. You will observe that instance mode works like singleton lifetime mode - the same object instance is supplied to all the requests.

READ MORE

In Part 1 and Part 2 of this series you developed a simple database driven application that displays a list of customers and also allows you to modify the customer details. Although the application is working as expected, it relies on the local instances of the NorthwindDbContext to get its job done. In this article we will use the Dependency Injection (DI) features of MVC 6 to inject the NorthwindDbContext into the controller class. Later we will also add repository support in the application.

Let's begin!

Open the same project in the Visual Studio and also open the Startup class in the IDE. Modify the ConfigureServices() method of the Startup class as shown below:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddEntityFramework()
            .AddSqlServer()
            .AddDbContext<NorthwindDbContext>
             (options => options.UseSqlServer
             (AppSettings.ConnectionString));
}

Notice the code marked in bold letters. We have chained the AddDbContext() method at the end of t he AddSqlServer() method call. The AddDbContext() is a generic method that allows you to specify the type of the DbContext that you wish to inject into the controllers. Moreover, you can also specify the database connection string while adding the DbContext. In this case the AppSettings.ConnectionString property holds the database connection string and is passed to the UseSqlServer() method.

Now open the NorthwindDbContext class and remove the OnConfiguring() overridden method completely.

public class NorthwindDbContext:DbContext
{
    public DbSet<Customer> Customers { get; set; }
    
    // no more OnConfiguring() method
}

We do this because connection string is now being supplied from the ConfigureServices() method.

Finally, open the HomeController and add a public constructor to it as shown below:

public class HomeController : Controller
{
    private NorthwindDbContext db;
    private CustomerRepository repository;

    public HomeController(NorthwindDbContext context)
    {
        this.db = context;
        this.repository = repository;
    }
...
...
}

The HomeController class declares a NorthwindDbContext variable at the top. This variable is assigned in the constructor. The constructor takes a parameter of type NorthwindDbContext. This parameter will be supplied by the DI framework of MVC 6. Once received we store its reference in the db variable for further use.

We need to modify the actions to use this class level variable instead of locally instantiating a DbContext. So, we need remove the using blocks from the existing code. The modified actions are shown below:

public IActionResult Index()
{
    List<Customer> data = db.Customers.ToList();
    return View(data);
}

public IActionResult Edit(string id)
{
    Customer data = db.Customers.Where(i => 
                    i.CustomerID == id).SingleOrDefault();
    var query = (from c in db.Customers
                    orderby c.Country ascending
                    select new SelectListItem() 
                    { Text = c.Country, Value 
                      = c.Country }).Distinct();
    List<SelectListItem> countries = query.ToList();
    ViewBag.Countries = countries;
    return View(data);
}

public IActionResult Save(Customer obj)
{
    var query = (from c in db.Customers
                    orderby c.Country ascending
                    select new SelectListItem() 
                    { Text = c.Country, Value = 
                      c.Country }).Distinct();
    List<SelectListItem> countries = query.ToList();
    ViewBag.Countries = countries;

    if (ModelState.IsValid)
    {
        db.Entry(obj).State = EntityState.Modified;
        db.SaveChanges();
    }
    return View("Edit", obj);
}

Run the application by setting a break point in the constructor. You will find that the DI framework supplies an instance of NorthwindDbContext configured to use the specified connection string.

So far so good. Now let's go ahead and use a repository for the database operations.

To do so, add CustomerRepository class in the Classes folder and write five methods - SelectAll(), SelectByID(), Insert(), Update() and Delete() - in it as shown below:

public class CustomerRepository
{
    public NorthwindDbContext Context {get; private set;}
    public CustomerRepository(NorthwindDbContext context)
    {
        this.Context = context;
    }

    public List<Customer> SelectAll()
    {
        return Context.Customers.ToList();
    }

    public Customer SelectByID(string id)
    {
        return Context.Customers.Where(i => 
               i.CustomerID == id).SingleOrDefault();
    }

    public void Insert(Customer obj)
    {
        Context.Entry(obj).State = EntityState.Added;
        Context.SaveChanges();
    }

    public void Update(Customer obj)
    {
        Context.Entry(obj).State = EntityState.Modified;
        Context.SaveChanges();
    }

    public void Delete(string id)
    {
        Customer obj = Context.Customers.Where(i => 
                   i.CustomerID == id).SingleOrDefault();
        Context.Entry(obj).State = EntityState.Deleted;
        Context.SaveChanges();
    }
}

The CustomerRepository class encapsulates all the logic to perform CRUD operations on the Customers table. Notice that the CustomerRepository doesn't create any local instance of NorthwindDbContext class. It receives it through the constructor. We do this because we want the DI framework to inject the DbContext in the repository just as it did for the controller. The received instance is stored in a public Context property. The Context property has private setter so that the DbContext can't be assigned from outside, at the same time allowing access to the DI supplied instance.

Once the CustomerRepository is ready you need to register it with the DI framework. This can be done as follows:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddEntityFramework()
            .AddSqlServer()
            .AddDbContext<NorthwindDbContext>
             (options => options.UseSqlServer
             (AppSettings.ConnectionString));
    services.AddScoped<CustomerRepository>();
}

Notice the code marked in bold letters. It uses the AddScoped() generic method to register CustomerRepository class with the DI framework. The AddScope() creates an object instance whose scope is the current request. So, now CustomerRepository can also be injected into the controller class.

Although the above class doesn't do that, you can create a repository interface and then implement that interface on the repository class. If your repository is based on some interface you can also specify the interface in the AddScoped() generic method. For example:

services.AddScoped<ICustomerRepository, CustomerRepository>();

The following code shows how the injected repository instance can be used.

public class HomeController : Controller
{
    private CustomerRepository repository;

    public HomeController(CustomerRepository repository)
    {
        this.repository = repository;
    }
...
...
}

This code should look familiar to you because you used it while injecting the NorthwindDbContext. In this case, however, you declare a variable of type CustomerRepository and assign it in the constructor.

Once injected you can use the CustomerRepository in all the actions instead of using the DbContext directly. The following code shows the modified actions of the HomeController.

public IActionResult Index()
{
    List<Customer> data = repository.SelectAll();
    return View(data);
}

public IActionResult Edit(string id)
{
    Customer data = repository.SelectByID(id);
    var query = (from c in repository.Context.Customers
                    orderby c.Country ascending
                    select new SelectListItem()
                    { Text = c.Country, Value = c.Country })
                    .Distinct();
    List<SelectListItem> countries = query.ToList();
    ViewBag.Countries = countries;
    return View(data);
}

public IActionResult Save(Customer obj)
{
    var query = (from c in repository.Context.Customers
                    orderby c.Country ascending
                    select new SelectListItem()
                    { Text = c.Country, Value = c.Country })
                    .Distinct();
    List<SelectListItem> countries = query.ToList();
    ViewBag.Countries = countries;

    if (ModelState.IsValid)
    {
        repository.Update(obj);
    }
    return View("Edit", obj);
} 

For the sake of simplicity the above code creates List of SelectListItem objects by writing a query using the Context property. You could have added some method in the repository that returned the desired countries. Run the application again. If all goes well, you should be able to see the customer list and will be able to modify the customer details.

READ MORE
...