Monday, April 30, 2012

ASP.NET MVC 3 - Using Ninject for Dependency Injection


This post is to show how to implement Dependency Injection in ASP.NET MVC 3 application with simple steps. For Details on Dependency Injection (DI) please refer to my article here.

For the sake of simplicity and to understand the idea, I have simply specified all the classes in same project(web application here). In real time scenario this might extend to multiple class libraries based on the nature of the project.

In our example, we're targeting a Mark sheet creation with minimal and predefined data. MarkSheetController expects a MarksheetModel and the MarkSheetModel expects a MarkEvaluator which we're going to achieve with Constructor injection as follows.

Implementaiton Details

First we need to install the Ninject.MVC3 Nuget package using Nuget manager. For details on Nuget manager please verify my post here. Upon installing the package, a file called NinjectWebCommon.cs will get created under the folder called App_Start.  In the  static method called RegisterServices, add the statement to register the dependencies with Ninject container. Following Snippets are self explanatory to understand the concept.

Snippet 1: (App_Start/NinjectWebCommon.cs => NinjectWebCommon.RegisterServices())

private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<IMarkSheetModel>().To<MarkSheetModel>();
    kernel.Bind<IMarkEvaluator>().To<MarkEvaluator>();
}

In the above snippet I've registered with Ninject that MarkSheetModel concrete class is bound to IMarkSheetModel and MarkEvaluator concrete class is bound to IMarkEvaluator interface. Based on this Ninject resolves the dependency by creating instance for the respective concrete class and pass it in the place of the related interface specification. Following snippet shows how we're making use of constructor injection to achieve that.

Snippet 2: (MarkSheetController - note the IMarkSheetModel parameter in the constructor)

public class MarkSheetController : Controller
{
    private IMarkSheetModel model;


    public MarkSheetController(IMarkSheetModel modelParam)
    {
        model = modelParam;
    }

    //
    // GET: /MarkSheet/

    public ActionResult Index()
    {
        model.LoadDetails();
        return View(model);
    }

}

Snippet 3: (MarkSheetController - note the IMarkEvaluator parameter in the constructor)

public class MarkSheetModel : IMarkSheetModel
{
    private IMarkEvaluator evaluator;

    public string StudentId { get; set; }
    public string StudentName { get; set; }
    public IEnumerable<Subject> SubjectList { get; set; }
    public int Total { get; private set; }
    public decimal Average { get; private set; }
    public string Grade { get; private set; }
    
    public MarkSheetModel(IMarkEvaluator evaluatorParam)
    {
        evaluator = evaluatorParam;
    }

    public void LoadDetails()
    {
        this.StudentId = "S1001";
        this.StudentName = "Sample";

        this.SubjectList = new List<Subject> { new Subject{ SubjectId="Sub0001", SubjectName ="Sub 1", Score = 78},
        new Subject{ SubjectId="Sub0002", SubjectName ="Sub 2", Score = 84},
        new Subject{ SubjectId="Sub0003", SubjectName ="Sub 3", Score = 72},
        new Subject{ SubjectId="Sub0004", SubjectName ="Sub 4", Score = 69}};

        this.Total = this.evaluator.CalculateTotal(this.SubjectList);
        this.Average = this.evaluator.CalculateAverage(this.SubjectList);
        this.Grade = this.evaluator.CalculateGrade(this.SubjectList);
    }

}

public interface IMarkSheetModel
{
    void LoadDetails();
    IEnumerable<Subject> SubjectList { get; set; }
    string StudentId { get; set; }
    string StudentName { get; set; }
}

public class Subject
{
    public string SubjectId { get; set; }
    public string SubjectName { get; set; }
    public int Score { get; set; }
}

Snippet 4:

public class MarkEvaluator : IMarkEvaluator
{
    public int CalculateTotal(IEnumerable<Subject> subjectList)
    {
        return subjectList.Sum(su => su.Score);
    }

    public decimal CalculateAverage(IEnumerable<Subject> subjectList)
    {
        return Convert.ToDecimal(CalculateTotal(subjectList)) / subjectList.Count();
    }

    public String CalculateGrade(IEnumerable<Subject> subjectList)
    {
        decimal averageScore = CalculateAverage(subjectList);

        if (averageScore > 80)
        {
            return "Grade A";
        }
        else if (averageScore > 70)
        {
            return "Grade B";
        }
        else if (averageScore > 60)
        {
            return "Grade C";
        }
        else
        {
            return "Grade D";
        }
    }
}

public interface IMarkEvaluator
{
    decimal CalculateAverage(IEnumerable<Subject> subjectList);
    string CalculateGrade(IEnumerable<Subject> subjectList);
    int CalculateTotal(IEnumerable<Subject> subjectList);
}


While executing the above snippets, the constructor of the MarkSheetController and MarkSheetModel is supplied with the respective instance.

References:
https://github.com/ninject/ninject.web.mvc/wiki/MVC3


3 comments:

  1. Error 1 The type 'MvcApplication1.Models.MarkEvaluator' cannot be used as type parameter 'TImplementation' in the generic type or method 'Ninject.Syntax.IBindingToSyntax.To()'. There is no implicit reference conversion from 'MvcApplication1.Models.MarkEvaluator' to 'MvcApplication1.Models.IMarkEvaluator'. D:\Aplicatii\IOC\MvcApplication1\MvcApplication1\App_Start\NinjectMVC3.cs 15 13 MvcApplication1

    ReplyDelete
  2. Great.. Tutorial is just awesome..It is really helpful for a newbie like me..
    Dot Net Training in Chennai | ASP.NET MVC Online Training

    ReplyDelete

Creative Commons License
This work by Tito is licensed under a Creative Commons Attribution 3.0 Unported License.