Home .NET ASP.NET MVC Lesson 2. Dependency Injection

ASP.NET MVC Lesson 2. Dependency Injection

by admin

Thepurpose of the lesson : The study of DI (Dependency Injection). Example on Ninject, Unity, Autofac, and Winsor.
In many cases, the same class instance is used in your application in different modules. A simple way to implement this is to use the Singleton pattern.
But let’s look at this situation from the other side. Since this object is created the first time it is accessed, we cannot control its lifetime. In unit-testing we don’t need to use this object (or it may not be possible). To avoid this, we do not call the object directly, but through an interface. Both the actual class instance and the stub instance for testing will implement this interface. And we leave the creation logic to the DI-container.
For example, before using the service. Let’s describe a couple of classes, the IWeapon interface with the Kill method, two implementation classes Bazuka and Sword, and the Warriorclass that uses the weapon :

public interface IWeapon{void Kill();}public class Bazuka : IWeapon{public void Kill(){Console.WriteLine("BIG BADABUM!");}}public class Sword : IWeapon{public void Kill(){Console.WriteLine("Chuk-chuck");}}public class Warrior{readonly IWeapon Weapon;public Warrior(IWeapon weapon){this.Weapon = weapon;}public void Kill(){Weapon.Kill();}}

Use this :

class Program{static void Main(string[] args){Warrior warrior = new Warrior(new Bazuka());warrior.Kill();Console.ReadLine();}}

Read between the lines. We create a warrior and give him a bazooka, he goes and kills. In the console we get :

BIG BADABUM!

Note that we have no check for null in the line

Weapon.Kill();

What’s wrong here? The warrior doesn’t know if he has a weapon, and it’s not a separate module that issues weapons, but the main program.
The point of the DI is to have another module do the weapons.
Connecting Ninject:

Install-Package Ninject

Create a module that deals with the issuing of weapons :

public class WeaponNinjectModule : NinjectModule{public override void Load(){this.Bind<IWeapon> ().To<Sword> ();}}

Which literally means : "If they ask for weapons, give them swords."
Create a "service locator" and use weapons :

class Program{public static IKernel AppKernel;static void Main(string[] args){AppKernel = new StandardKernel(new WeaponNinjectModule());var warrior = AppKernel.Get<Warrior> ();warrior.Kill();Console.ReadLine();}}

As you can see, we do not create the warrior object with the new construct, but with AppKernel.Get<> () When we create an AppKernel, we pass in the module responsible for issuing weapons (in this case, a sword) as a constructor. Any object that we try to get through the AppKernel.Get. will be (if possible) initialized, if there are modules that know how to do this.
Another point of application is when the object Warrior does not take the weapon with it every time, but when it is not detected it calls the locator service and gets it :

public class OtherWarrior{private IWeapon _weapon;public IWeapon Weapon{get{if (_weapon == null){_weapon = Program.AppKernel.Get<IWeapon> ();}return _weapon;}}public void Kill(){Weapon.Kill();}}

Execute :

var otherWarrior = new OtherWarrior();otherWarrior.Kill();

Our warrior gets weapons by direct supply – super!
Ninject has one more very nice detail. If the property ( public property ) is labeled [Inject] , then when you create a class with AppKernel.Get<> () – The field is initialized by the service locator :

public class AnotherWarrior{[Inject]public IWeapon Weapon { get; set; }public void Kill(){Weapon.Kill();}}var anotherWarrior = AppKernel.Get<AnotherWarrior> ();anotherWarrior.Kill();

Unity

Exactly the same :

  • Installing
    Install-Package Unity

  • Container service initialization
    Container = new UnityContainer();

  • Type registration
    Container.RegisterType(typeof(IWeapon), typeof(Bazuka));

  • Obtaining an object and using it :
    var warrior = Container.Resolve<Warrior> ();warrior.Kill();
  • Unity also has a single class (Singleton) ServiceLocator which registers a container and allows you to access services from anywhere.
    var serviceProvider = new UnityServiceLocator(Container);ServiceLocator.SetLocatorProvider(() => serviceProvider);

  • Wily OtherWarrior now this is how the weapon gets :
    public class OtherWarrior{private IWeapon _weapon;public IWeapon Weapon{get{if (_weapon == null){_weapon = ServiceLocator.Current.GetInstance<IWeapon> ();}return _weapon;}}public void Kill(){Weapon.Kill();}}

Autofac

This is actually how it works :

  • Installing
    Install-Package Autofac
  • Initialization of the service-locator builder ( ContainerBuilder ) – no, no, it’s not the container itself, it’s like modules
    var builder = new ContainerBuilder();
  • Type Registration. It is necessary to register all necessary classes, because creating instances of unregistered classes is not implemented here.
    builder.RegisterType<Bazuka> ();builder.RegisterType<Warrior> ();builder.Register<IWeapon> (x => x.Resolve<Bazuka> ());

  • Creating a locator service (Container)
    var container = builder.Build();

  • Obtaining an object and using it :
    var warrior = container.Resolve<Warrior> ();warrior.Kill();

Castle Windsor

  • Installing
    Install-Package Castle.Windsor

  • Initialization of the locator service
    var container = new WindsorContainer();

  • Type Registration. Similar to Autofac.
    container.Register(Component.For<IWeapon> ().ImplementedBy<Bazuka> (), Component.For<Warrior> ().ImplementedBy<Warrior> ());

  • Obtaining an object and using it :
    var warrior = container.Resolve<Warrior> ();warrior.Kill();

A small subtotal

Actually, Dependency Injection implementations don’t differ much, but they do. Some support initialization in Web.config (App.config) files. Some, set rules for initialization, as we will now look at the Ninject extension for asp.net mvc – this refers to initializing the service-locator as a generic object generator, and separately for each thread or web request.

Area Objects (Ninject)

In Ninject, you can specify several ways to initialize getting an object from a class. If we work in different contexts (e.g., in different threads), the objects must be used differently. In this way, the scalability and flexibility of the application is maintained.

Area

Binding method Explanation
Temporary InTransientScope() A class object will be created on each demand (default method).
A loner InSingletonScope() The class object will be created once and reused.
Stream InThreadScope() One object per stream.
Request InRequestScope() There will be one object per web request

Lifetime Manager in Unity

Unity uses an implementation of the abstract class LifetimeManager for the task of initialization rules.
What happens is this :

_container.RegisterType<DbContext, SavecashTravelContext> (new PerRequestLifetimeManager());

Where PerRequestLifetimeManager is an implementation of LifetimeManager:

public class PerRequestLifetimeManager : LifetimeManager{/// <summary> /// Key to store data/// </summary> private readonly string _key = String.Format("SingletonPerRequest{0}", Guid.NewGuid());/// <summary> /// Retrieve a value from the backing store associated with this Lifetime policy./// </summary> /// <returns> /// the object desired, or null if no such object is currently stored./// </returns> public override object GetValue(){if (HttpContext.Current != null HttpContext.Current.Items.Contains(_key))return HttpContext.Current.Items[_key];return null;}/// <summary> /// Stores the given value into backing store for retrieval later./// </summary> /// <param name="newValue"> The object being stored.</param> public override void SetValue(object newValue){if (HttpContext.Current != null)HttpContext.Current.Items[_key] = newValue;}/// <summary> /// Remove the given object from backing store./// </summary> public override void RemoveValue(){if (HttpContext.Current!= null HttpContext.Current.Items.Contains(_key))HttpContext.Current.Items.Remove(_key);}}

Bottom line. All objects are stored in HttpContext.Current.Items[_key] and are output only if they are already in the same context ( HttpContext.Current ). Otherwise, a new object is created. If the current context ( HttpContext.Current ) in the code area does not exist (we use this LifetimeManager in a console application or in a separate thread) – then this container will not work.

Using Ninject in asp.net mvc

Install Ninject in the asp.net mvc environment. Separately we create our own LessonProject, create a HomeController with method and view Index there. (/Contollers/HomeController.cs):

public class HomeController :Controller{public ActionResult Index(){return View();}}

And (/Views/Home/Index.cshtml):

@{ViewBag.Title = "LessonProject";Layout = "~/Views/Shared/_Layout.cshtml";}<h2> LessonProject</h2>

We launch it – it works.
Note : We will carry this project forward in future lessons.
Now let’s install the Ninject module and Ninject.MVC3 for this project.

Install-Package Ninject.MVC3

Add the class to the App_Start folder (/App_Start/NinjectWebCommon.cs):

[assembly: WebActivator.PreApplicationStartMethod(typeof(LessonProject.App_Start.NinjectWebCommon), "Start")][assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(LessonProject.App_Start.NinjectWebCommon), "Stop")]namespace LessonProject.App_Start{using System;using System.Web;using Microsoft.Web.Infrastructure.DynamicModuleHelper;using Ninject;using Ninject.Web.Common;public static class NinjectWebCommon{private static readonly Bootstrapper bootstrapper = new Bootstrapper();/// <summary> /// Starts the application/// </summary> public static void Start(){DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));bootstrapper.Initialize(CreateKernel);}/// <summary> /// Stops the application./// </summary> public static void Stop(){bootstrapper.ShutDown();}/// <summary> /// Creates the kernel that will manage your application./// </summary> /// <returns> The created kernel.</returns> private static IKernel CreateKernel(){var kernel = new StandardKernel();kernel.Bind<Func<IKernel> > ().ToMethod(ctx => () => new Bootstrapper().Kernel);kernel.Bind<IHttpModule> ().To<HttpApplicationInitializationHttpModule> ();RegisterServices(kernel);return kernel;}/// <summary> /// Load your modules or register your services here!/// </summary> /// <param name="kernel"> The kernel.</param> private static void RegisterServices(IKernel kernel){}}}

In RegisterServices we add the initialization of our services. First we add a funny IWeapon, and later on we will come back to this method to register other services:

public interface IWeapon{string Kill();}…public class Bazuka : IWeapon{public string Kill(){return "BIG BADABUM!";}}…private static void RegisterServices(IKernel kernel){kernel.Bind<IWeapon> ().To<Bazuka> ();}

In the controller we use the attribute [Inject] :

public class HomeController : Controller{[Inject]public IWeapon weapon { get; set; }public ActionResult Index(){return View(weapon);}}

Changing View:

@model LessonProject.Models.IWeapon@{ViewBag.Title = "LessonProject";Layout = "~/Views/Shared/_Layout.cshtml";}<h2> LessonProject</h2> <p> @Model.Kill()</p>

The output is :
ASP.NET MVC Lesson 2. Dependency Injection
Ninject uses WebActivator:

  • registers its OnePerRequestHttpModule and NinjectHttpModule modules
  • creates StandartKernel
  • initializes our services.

DependencyResolver

The DependencyResolver class appeared in asp.net mvc3. This class provides a service instance retrieval. We can also retrieve our registered services (and even the DI-container in use) through this class.

public class HomeController : Controller{private IWeapon weapon { get; set; }public HomeController(){weapon = DependencyResolver.Current.GetService<IWeapon> ();}public ActionResult Index(){return View(weapon);}}

Result

The use of DI containers in modern applications is necessary to get rid of strong code cohesion, and for easy access from any part of the code to services. Also, it is necessary for writing Unit tests.
All the sources are at https://bitbucket.org/chernikov/lessons

You may also like