Thursday 27 December 2012

Dependency Injection in MVC


ASP.NET MVC 3 Dependency Injection

Version: 1.1.0

Description

In Object Oriented Programming paradigm, objects work together in a collaboration model where there are contributors and consumers. Naturally, this communication model generates dependencies between objects and components that could became difficult to manage when complexity increases . You have probably heard about Factory Pattern and the separation between the interface and the implementation using services. However, the client objects are often responsible for service location. Before introducing the Dependency Injection Pattern, we will explain what Inversion of Control (IoC) principle is. With Inversion of Control (Ioc), consumer objects do not create the other objects on which they rely. Those objects come from an external source.

Overview

Note:
This Hands-on Lab assumes you have basic knowledge of ASP.NET MVC and ASP.NET MVC 3 filters. If you have not used ASP.NET MVC 3 filters before, we recommend you to go overASP.NET MVC Custom Action Filters and MVC Global and Dynamic Action filters Hand-on Lab.
In Object Oriented Programming paradigm, objects work together in a collaboration model where there are contributors and consumers. Naturally, this communication model generates dependencies between objects and components that could became difficult to manage when complexity increases .
Figure 1
Class dependencies and model complexity
You have probably heard about the Factory Pattern and the separation between the interface and the implementation using services. However, the client objects are often responsible for service location.
Before introducing the Dependency Injection Pattern, we will explain what Inversion of Control (IoC) principle is.
With Inversion of Control (Ioc), consumer objects do not create the other objects on which they rely. Those objects come from an external source.

The Dependency Injection (DI) Design Pattern

Dependency injection (DI) design pattern is based on separating component behavior from dependency resolution without object intervention.
This pattern is a particular implementation of Inversion of Control, where the consumer object receives his dependencies inside constructor properties or arguments.
DI requires a framework component behind to deal with class constructor.
Figure 2
Overview – Dependency Injection diagram
The advantages of using Dependency Injection pattern and Inversion of Control are the following:
  • Reduces class coupling
  • Increases code reusing
  • Improves code maintainability
  • Improves application testing
Note:
Depencency Injection is sometimes compared with Abstract Factory Design Pattern, but there is a slight difference between both approaches. DI has a Framework working behind to solve dependencies by calling the factories and the registered services.
Now that you understand the Dependency Injection Pattern, you will learn through this lab how to apply it on ASP.NET MVC 3. You will start using Dependency Injection on Controllers to include a service for database access. Next you will use Dependency Injection on Views to use a service inside a view and display information. Then, you will extend DI to MVC 3 Filters concept and inject a Custom Action Filter in the solution.
In this Hands-on Lab, you will learn how to:
  • Integrate MVC 3 with Unity Application Block for Dependency Injection
  • Use dependency injection inside an MVC Controller
  • Use dependency injection inside an MVC View
  • Use dependency injection inside an MVC Action Filter
Note:
This Lab proposes Unity Application Block as the dependency resolver framework, but it is posible to adapt any Dependency Injection Framework to work with MVC 3.

System Requirements

You must have the following items to complete this lab:
  • ASP.NET and ASP.NET MVC 3
  • Visual Studio 2010
  • SQL Server Database (Express edition or above)
    Note:
    You can install the previous system requirements by using the Web Platform Installer 3.0:http://go.microsoft.com/fwlink/?LinkID=194638.

Exercises

This Hands-On Lab is comprised by the following exercises:
  1. Exercise 1: Injecting a Controller
  2. Exercise 2: Injecting a View
  3. Exercise 3: Injecting Filters
Estimated time to complete this lab: 30 minutes.
Note:
Each exercise is accompanied by an End folder containing the resulting solution you should obtain after completing the exercises. You can use this solution as a guide if you need additional help working through the exercises.


 Injecting a Controller
In this exercise, you will learn how to use Dependency Injection in MVC Controllers, by integrating Unity Application Block. For that reason you will include services into your MVC Music Store controllers to separate the logic from the data access. The service will create a new dependence into the controller constructor that will be resolved using Dependency Injection with the help of Unity application block.
With this approach you will learn how to generate less coupled applications, which are more flexible and easier to maintain and test. Additionally, you will also learn how to integrate MVC with Unity.

About StoreManager Service

The MVC Music Store provided in the begin solution now includes a service that manages the Store Controller data, StoreService. Below you will find the Store Service implementation. Note that all the methods return Model entities.
C# - StoreService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MvcMusicStore.Models;
namespace MvcMusicStore.Services
{
    public class StoreService : MvcMusicStore.Services.IStoreService
    {
        MusicStoreEntities storeDB = new MusicStoreEntities();
        public IList<string> GetGenreNames()
        {
            var genres = from genre in storeDB.Genres
                         select genre.Name;
            return genres.ToList();
        }
        public Genre GetGenreByName(string name)
        {
            var genre = storeDB.Genres.Include("Albums")
                    .Single(g => g.Name == name);
            return genre;
        }
        public Album GetAlbum(int id)
        {
            var album = storeDB.Albums.Single(a => a.AlbumId == id);
            return album;
        }
    }
}
Visual Basic - StoreService.vb
Public Class StoreService
    Implements IStoreService
Private storeDB As New MusicStoreEntities
    Public Function GetGenreNames() As IList(Of String) Implements IStoreService.GetGenreNames
        Dim genres = From genre In storeDB.Genres
                     Select genre.Name
        Return genres.ToList()
    End Function
    Public Function GetGenreByName(ByVal name As String) As Genre Implements IStoreService.GetGenreByName
        Dim genre = storeDB.Genres.Include("Albums").Single(Function(g) g.Name = name)
        Return genre
    End Function
    Public Function GetAlbum(ByVal id As Integer) As Album Implements IStoreService.GetAlbum
        Dim album = storeDB.Albums.Single(Function(a) a.AlbumId = id)
        Return album
    End Function
End Class
Additionally, in the StoreController you will find in the begin solution now uses StoreService. All data references were removed from Store Controller, and therefore it is possible to modify the current data access provider without making changes at any method that consumes the Store Service.
You will find below that the Store Controller implementation has a dependency with the Store Service inside the class constructor.
Note:
The dependency introduced in this exercise is related to MVC Inversion of Control (IoC).
The StoreController class constructor receives an IStoreService parameter, which is essential to perform service calls inside the class. However, StoreController does not implement the default constructor (with no parameters) that any controller must have to work with IoC.
To resolve the dependency, the controller should be created by an abstract factory (a class that returns any object of the specified type).
C# - StoreController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcMusicStore.ViewModels;
using MvcMusicStore.Models;
using MvcMusicStore.Services;
        private IStoreService service;
            this.service = service;
            var genres = this.service.GetGenreNames();
            var genreModel = this.service.GetGenreByName(genre);
            var album = this.service.GetAlbum(id);
namespace MvcMusicStore.Controllers
{
    public class StoreController : Controller
    {
using MvcMusicStore.ViewModels;
using MvcMusicStore.Models;
using MvcMusicStore.Services;
        private IStoreService service;
            this.service = service;
            var genres = this.service.GetGenreNames();
            var genreModel = this.service.GetGenreByName(genre);
            var album = this.service.GetAlbum(id);
        public StoreController(IStoreService service)
        {
using MvcMusicStore.ViewModels;
using MvcMusicStore.Models;
using MvcMusicStore.Services;
        private IStoreService service;
            this.service = service;
            var genres = this.service.GetGenreNames();
            var genreModel = this.service.GetGenreByName(genre);
            var album = this.service.GetAlbum(id);
        }
        //
        // GET: /Store/
        public ActionResult Index()
        {
            // Create list of genres
using MvcMusicStore.ViewModels;
using MvcMusicStore.Models;
using MvcMusicStore.Services;
        private IStoreService service;
            this.service = service;
            var genres = this.service.GetGenreNames();
            var genreModel = this.service.GetGenreByName(genre);
            var album = this.service.GetAlbum(id);
            // Create your view model
            var viewModel = new StoreIndexViewModel
            {
                Genres = genres.ToList(),
                NumberOfGenres = genres.Count()
            };
            return View(viewModel);
        }
        //
        // GET: /Store/Browse?genre=Disco
        public ActionResult Browse(string genre)
        {
using MvcMusicStore.ViewModels;
using MvcMusicStore.Models;
using MvcMusicStore.Services;
        private IStoreService service;
            this.service = service;
            var genres = this.service.GetGenreNames();
            var genreModel = this.service.GetGenreByName(genre);
            var album = this.service.GetAlbum(id);
            var viewModel = new StoreBrowseViewModel()
            {
                Genre = genreModel,
                Albums = genreModel.Albums.ToList()
            };
            return View(viewModel);
        }
        //
        // GET: /Store/Details/5
        public ActionResult Details(int id)
        {
using MvcMusicStore.ViewModels;
using MvcMusicStore.Models;
using MvcMusicStore.Services;
        private IStoreService service;
            this.service = service;
            var genres = this.service.GetGenreNames();
            var genreModel = this.service.GetGenreByName(genre);
            var album = this.service.GetAlbum(id);
            return View(album);
        }
    }
}
Visual Basic - StoreController.vb
Public Class StoreController
    Inherits Controller
    Private service As IStoreService
        Me.service = service
        Dim genres = Me.service.GetGenreNames()
        Dim genreModel = Me.service.GetGenreByName(genre)
        Dim album = Me.service.GetAlbum(id)
    Public Sub New(ByVal service As IStoreService)
    Private service As IStoreService
        Me.service = service
        Dim genres = Me.service.GetGenreNames()
        Dim genreModel = Me.service.GetGenreByName(genre)
        Dim album = Me.service.GetAlbum(id)
    End Sub
    '
    'GET: /Store/
    Public Function Index() As ActionResult
        'Create list of genres
    Private service As IStoreService
        Me.service = service
        Dim genres = Me.service.GetGenreNames()
        Dim genreModel = Me.service.GetGenreByName(genre)
        Dim album = Me.service.GetAlbum(id)
        'Create your view model
        Dim viewModel = New StoreIndexViewModel With
                        {.Genres = genres.ToList(),
                         .NumberOfGenres = genres.Count()}
        Return View(viewModel)
    End Function
    '
    'GET: /Store/Browse?genre=Disco
    Public Function Browse(ByVal genre As String) As ActionResult
    Private service As IStoreService
        Me.service = service
        Dim genres = Me.service.GetGenreNames()
        Dim genreModel = Me.service.GetGenreByName(genre)
        Dim album = Me.service.GetAlbum(id)
        Dim viewModel = New StoreBrowseViewModel With
                        {.Genre = genreModel,
                         .Albums = genreModel.Albums.ToList()}
        Return View(viewModel)
    End Function
    '
    'GET: /Store/Details/5
    Public Function Details(ByVal id As Integer) As ActionResult
    Private service As IStoreService
        Me.service = service
        Dim genres = Me.service.GetGenreNames()
        Dim genreModel = Me.service.GetGenreByName(genre)
        Dim album = Me.service.GetAlbum(id)
        Return View(album)
    End Function
End Class
Note:
You will get an error when a class tries to create this Store Controller without sending the service interface, because there is not a parameterless constructor declared.Through this lab you will learn how to deal with this problem using Dependency Injection with Unity.

Task 1 – Running the Application

In this task, you will run the Begin application, which is now including the service into the Store Controller that separates the data access from the application logic.
After browsing to the store you will receive an exception since the controller service is not passed as a parameter by default:
  1. Open the begin solution MvcMusicStore.sln at Source\Ex01-Injecting Controller\Begin.
  2. Press F5 to run the application.
  3. Browse to /Store to load Store Controller. You will get the error message “No parameterless constructor defined for this object”:
    Figure 1
    Error while running MVC Begin Application
  4. Close the browser.
In the following steps you will work on the Music Store Solution to inject the dependency this controller needs.

Task 2 – Including Unity into MvcMusicStore Solution

In this task, you will include Unity Application Block 2.0 into your solution.
Note:
The Unity Application Block (Unity) is a lightweight, extensible dependency injection container with optional support for instance and type interception. It’s a general-purpose container for use in any type of .NET application. It provides all the common features found in dependency injection mechanisms including: object creation, abstraction of requirements by specifying dependencies at runtime and flexibility, be deferring the component configuration to the container.
You could read more about Unity 2.0 at msdn.
  1. Open the begin solution MvcMusicStore.sln at Source\Ex01-Injecting Controller\Begin.
  2. In the MvcMusicStore project, add a reference to Microsoft.Practices.Unity.dll, which is included in Source\Assets\Unity 2.0\ folder of this lab.

Task 3 – Adding a Unity Controller Factory

In this task, you will add to the solution a custom controller factory for Unity. This class implements IControllerFactory interface, extending CreateController and ReleaseControllermethods to work with Unity. This factory will create the instances of the controllers that work with Dependency Injection.
Note:
A controller factory is an implementation of the IControllerFactory interface, which is responsible both for locating a controller type and for instantiating an instance of that controller type.
The following implementation of CreateController finds the controller by name inside the Unity container and returns an instance if it was found. Otherwise, it delegates the creation of the controller to an inner factory. One of the advantages of this logic is that controllers can be registered by name.
You can find IControllerFactory interface reference at msdn.
  1. In the MvcMusicStore project, create a new folder named Factories, and add theUnityControllerFactory class, which is included in the Source\Assets[C#|VB] folder of this lab.
    C# - UnityControllerFactory.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using Microsoft.Practices.Unity;
    using System.Web.Routing;
    
    namespace MvcMusicStore.Factories
    {
        public class UnityControllerFactory : IControllerFactory
        {
            private IUnityContainer _container;
            private IControllerFactory _innerFactory;
    
            public UnityControllerFactory(IUnityContainer container)
                : this(container, new DefaultControllerFactory())
            {
            }
    
            protected UnityControllerFactory(IUnityContainer container, IControllerFactory innerFactory)
            {
                _container = container;
                _innerFactory = innerFactory;
            }
    
            public IController CreateController(RequestContext requestContext, string controllerName)
            {
                try
                {
                    return _container.Resolve<IController>(controllerName);
                }
                catch (Exception)
                {
                    return _innerFactory.CreateController(requestContext, controllerName);
                }
            }
    
            public void ReleaseController(IController controller)
            {
                _container.Teardown(controller);
            }
    
            public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
            {
                return System.Web.SessionState.SessionStateBehavior.Default;
            }
        }
    }
    Visual Basic - UnityControllerFactory.vb
    Imports Microsoft.Practices.Unity
    
    Public Class UnityControllerFactory
        Implements IControllerFactory
    
        Private _container As IUnityContainer
        Private _innerFactory As IControllerFactory
    
        Public Sub New(ByVal container As IUnityContainer)
            Me.New(container, New DefaultControllerFactory)
        End Sub
    
        Protected Sub New(ByVal container As IUnityContainer,
                          ByVal innerFactory As IControllerFactory)
    
            _container = container
            _innerFactory = innerFactory
        End Sub
    
        Public Function CreateController(ByVal requestContext As RequestContext,
                                         ByVal controllerName As String
                                         ) As IController Implements IControllerFactory.CreateController
    
            Try
                Return _container.Resolve(Of IController)(controllerName)
            Catch e1 As Exception
                Return _innerFactory.CreateController(requestContext, controllerName)
            End Try
        End Function
    
        Public Sub ReleaseController(ByVal controller As IController) Implements IControllerFactory.ReleaseController
            _container.Teardown(controller)
        End Sub
    
        Public Function GetControllerSessionBehavior(ByVal requestContext As RequestContext, ByVal controllerName As String) As System.Web.SessionState.SessionStateBehavior Implements IControllerFactory.GetControllerSessionBehavior
    
            Return System.Web.SessionState.SessionStateBehavior.Default
        End Function
    
    End Class
    Note:
    This factory class can be reused in any project that uses Dependency Injection for Controllers.

Task 4 – Registering Unity in Global.asax.[cs|vb] Application_Start

In this task, you will register Unity library into Global.asax.[cs|vb] Application Start.
  1. Open Global.asax.[cs|vb] file.
  2. Include Microsoft.Practices.Unity Application Block, and references to the namespacesServicesFactories and Controllers:
    (Code Snippet – ASP.NET MVC Dependency Injection – Ex1 Injecting Controllers Global using– Csharp)
    C#
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    1. using Microsoft.Practices.Unity;
    2. using MvcMusicStore.Services;
    3. using MvcMusicStore.Factories;
    4. using MvcMusicStore.Controllers;
    (Code Snippet – ASP.NET MVC Dependency Injection – Ex1 Injecting Controllers Global using– VB)
    Visual Basic
    Imports System
    Imports System.Collections.Generic
    Imports System.Linq
    Imports System.Web
    Imports System.Web.Mvc
    Imports System.Web.Routing
    Imports Microsoft.Practices.Unity
  3. Create a new Unity Container in Global.asax.[cs|vb]Application_Start and register the Store Service and the Store Controller.
    (Code Snippet – ASP.NET MVC Dependency Injection –Ex1 Injecting Controllers Unity Container – Csharp)
    C#
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);
    1.     var container = new UnityContainer();
    2.     container.RegisterType<IStoreService, StoreService>();
    3.     container.RegisterType<IController, StoreController>("Store");
    }
    (Code Snippet – ASP.NET MVC Dependency Injection –Ex1 Injecting Controllers Unity Container – VB)
    Visual Basic
    Protected Sub Application_Start()
        AreaRegistration.RegisterAllAreas()
        RegisterGlobalFilters(GlobalFilters.Filters)
        RegisterRoutes(RouteTable.Routes)
        Dim container = New UnityContainer
        container.RegisterType(Of IStoreService, StoreService)()
        container.RegisterType(Of IController, StoreController)("Store")
    End Sub
  4. Register a UnityControllerFactory of the previous container inside MVC ControllerBuilder as the current factory for the controllers:
    (Code Snippet – ASP.NET MVC Dependency Injection – Ex1 Injecting Controllers Global application start – Csharp)
    C#
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);
        var container = new UnityContainer();
        container.RegisterType<IStoreService, StoreService>();
        container.RegisterType<IController, StoreController>("Store");
    1.     var factory = new UnityControllerFactory(container);
    2.     ControllerBuilder.Current.SetControllerFactory(factory);
    }
    (Code Snippet – ASP.NET MVC Dependency Injection – Ex1 Injecting Controllers Global application start – VB)
    Visual Basic
    Protected Sub Application_Start()
        AreaRegistration.RegisterAllAreas()
        RegisterGlobalFilters(GlobalFilters.Filters)
        RegisterRoutes(RouteTable.Routes)
        Dim container = New UnityContainer
        container.RegisterType(Of IStoreService, StoreService)()
        container.RegisterType(Of IController, StoreController)("Store")
        Dim factory = New UnityControllerFactory(container)
        ControllerBuilder.Current.SetControllerFactory(factory) 
    End Sub
    Note:
    ControllerBuilder is an MVC class responsible for dynamically building a controller.
    You can read more about ControllerBuilder at msdn.

Task 5 – Running the Application

In this task, you will run the application to verify that the Store can now be loaded after including Unity.
  1. Press F5 to run the application.
  2. Browse to /Store. This will invoke StoreController, which is now created by usingUnityControllerFactory.
    Figure 2
    MVC Music Store
  3. Close the browser.
In the following exercises you will learn how to extend the Dependency Injection scope, and use it inside MVC Views and Action Filters.

1 comment:

If any doubt?then please comment in my post

How to reduce angular CLI build time

 Index: ----- I am using angular cli - 7 and I am going to tell how to reduce the build time as per my knowledge. Problem: -------- Now the ...