Search
Categories
Navigation
Twitter Updates

Twitter Updates

    follow me on Twitter
    « Anonymous Types in ASP.NET MVC | Main | Getting Started with Git on Windows »
    Friday
    Aug072009

    Using Ninject 2 with ASP.NET MVC

    Jet engine test; fuel injection at work - Photo by: DVIDSHUB (Flickr) As interest in test isolation and modularity of applications becomes more common within the .NET community, a growing interest in dependency injection has resulted in several inversion of control containers. One such framework is Nate Kohari’s Ninject, which is nearing a 2.0 release. This post will walk-through using Ninject 2 within ASP.NET MVC 1.0, using it for injection in controllers and action filters.

    Getting & Building the Source

    Since the final version of Ninject 2 hasn’t been released yet and the beta from February is a bit dated at this point, the first thing to do is to clone the project’s GitHub repository. We’ll want to get both the core Ninject source as well as the Ninject.Web.Mvc project:

    If you’re not familiar with Git, check out my previous post on using Git with Windows. Keep in mind that if we wanted to actually contribute to Ninject we’d do things a little bit differently, but in this instance we only want to download the source and use it, so we’ll just clone the repositories.

    Once we’ve got the source on our local machine, building Ninject is as easy as double-clicking the build-release.cmd file, which will generate a /build directory with the binaries in them. For Ninject.Web.Mvc you’ll need to open it in Visual Studio and build the solution in release mode with the binaries being put into the standard /build/release location.

    Problem Scenario

    For this example, we’ll build off my previous global view data post, but I’ve switched things up some to make it a bit more interesting. In this example, we’ve got some data that’s used by the Master Page in the footer on every view, namely a copyright notice and Google Analytics ID.

    Note: You can download the starting point for the project which is described in the next few sections. We’re starting with a stock MVC project and adding a few models, a partial view and an action filter to tie it all together.

    Models

    Since we expect to have to update these two pieces of data frequently (for some reason) we want to store them in the AppSettings section of Web.config.

    <appSettings>
      <add key="footer.analytics.id" value="UA-1234567"/>
      <add key="footer.copyright.template" value="Copyright 2009, All rights reserved" />
    </appSettings>

    To expose this data to the view, we’ve created an IFooterSettings interface and its corresponding FooterSettings implementation class.

    public interface IFooterSettings
    {
        string AnalyticsId { get; }
        string CopyrightNotice { get; }
    }
    
    public class FooterSettings : IFooterSettings
    {
        public FooterSettings( string analyticsId, string copyrightNotice)
        {
            AnalyticsId = analyticsId ?? string.Empty;
            CopyrightNotice = copyrightNotice ?? string.Empty;
        }
    
        public string AnalyticsId { get; protected set; }
    
        public string CopyrightNotice { get; protected set; }
    }

    To retrieve the FooterSettings object, there’s a FooterSettingsProvider class which reads the settings from a NameValueCollection and instantiates the settings object.

    public class FooterSettingsProvider
    {
        private static readonly string GoogleAnalyticsIdKey = "footer.analytics.id";
        private static readonly string CopyrightNoticeKey = "footer.copyright.template";
    
        public FooterSettingsProvider(NameValueCollection settings)
        {
            Settings = settings;
        }
    
        protected NameValueCollection Settings { get; set; }
    
        public IFooterSettings Create()
        {
            var analyticsId = Settings[GoogleAnalyticsIdKey] ?? string.Empty;
            var copyrightNotice = Settings[CopyrightNoticeKey] ?? string.Empty;
    
            return new FooterSettings(analyticsId, copyrightNotice);
        }
    }

    Views

    On the view front, there’s a strongly-typed partial view, Footer.ascx, which takes an IFooterSettings interface as its model. There’s nothing particularly interesting about this view, it simply outputs our settings data:

    <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IFooterSettings>" %>
    <div id="CopyrightNotice">
        <%= Html.Encode(Model.CopyrightNotice) %>
    </div>
    <script type="text/javascript">
        var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
        document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
    </script>
    <script type="text/javascript">
        var pageTracker = _gat._getTracker("<%= Html.Encode(Model.AnalyticsId) %>");
        pageTracker._initData();
        pageTracker._trackPageview();
    </script>

    The Site.Master renders the footer partial within the footer div, passing the IFooterSettings object stored in the footerSettings key of ViewData.

    <div id="footer">
        <% Html.RenderPartial("Footer", ViewData["footerSettings"]); %>
    </div>

    Controllers & Action Filters

    The glue to hold all this together is a custom action filter, ProvideFooterSettings, which uses the FooterSettingsProvider to create an IFooterSettings and adds it to the ViewData dictionary under the footerSettings key.

    public class ProvideFooterSettingsAttribute : ActionFilterAttribute
    {
        private IFooterSettings _settings;
    
        public IFooterSettings Settings
        {
            get
            {
                if (_settings == null)
                {
                    var provider = new FooterSettingsProvider(WebConfigurationManager.AppSettings);
                    _settings = provider.Create();
                }
                return _settings;
            }
            set
            {
                _settings = value;
            }
        }
    
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            filterContext.Controller.ViewData.Add("footerSettings", Settings);
        }
    }

    The ProvideFooterSettings attribute is then applied at the class level for both the HomeController and AcccountController classes, invoking the action filter for all actions on the controller and making the footer settings available to the partial view globally.

    With all that in place, the application runs and functions as expected, with the settings from Web.config/appSettings being passed to the partial view using IFooterSettings via the action filter.

    Dependency Injection Cometh

    This design is fairly decoupled, making future changes to individual components relatively easy to implement, Old, rusting train coupling; Photo by: hyku (Flickr)especially for such a simple requirement. There’s still some direct dependencies involved though, so we’ll add Ninject to the mix to reduce them. Remember, the goal of dependency injection is to decouple components so that they can be swapped out as requirements change with the additional benefit of enabling discrete unit testing.

    The main spot where everything is tied together is ProvideFooterSettingsAttribute, which has a dependency on FooterSettingsProvider and WebConfigurationManager. FooterSettingsProvider also takes a direct dependency on FooterSettings, but since it’s a factory object, we’ll leave it as is.

    Injecting Some Injection

    First, we’ll need to add references to Ninject and Ninject.Web.Mvc in our project. Since we’d like to decouple the dependencies in the ProvideFooterSettingsAttribute action filter, we need to wire up Ninject’s MVC integration, which is done by changing the base class of the Global.asax code-behind (MvcApplication).

    public class MvcApplication : NinjectHttpApplication
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
            routes.MapRoute(
                "Default",                                              // Route name
                "{controller}/{action}/{id}",                           // URL with parameters
                new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
            );
    
        }
    
        protected override void OnApplicationStarted()
        {
            RegisterRoutes(RouteTable.Routes);
            RegisterAllControllersIn(Assembly.GetExecutingAssembly());
        }
    
        protected override IKernel CreateKernel()
        {
            return new StandardKernel();
        }
    }

    You can see that Ninject’s current model of integrating with MVC requires a few changes to the structure of the MvcApplication class. It uses OnApplicationStarted instead of Application_Start because the NinjectHttpApplication has implemented Application_Start itself. What the base class is doing there is registering the NinjectControllerFactory, so that Ninject will inject dependencies into your controllers. Additionally, this controller factory uses the NinjectActionInvoker to inject action filters when they are invoked. Line 18 is registering all controllers in the currently executing assembly for injection, so we don’t have to tell Ninject which controllers to handle injection for.

    The other important bit is the CreateKernel method where we’re instantiating the Ninject kernel, which is like a super-factory that is used to perform injection. Right now we’re not configuring anything so the kernel won’t do much, but we’ll come back to it in a moment.

    Configuring the Kernel

    To break the dependencies in ProvideFooterSettingsAttribute, we want to inject the IFooterSettings object into it, removing the object’s knowledge about how its created. First, we’ll alter the class to remove the dependencies.

    public class ProvideFooterSettingsAttribute : ActionFilterAttribute
    {
        [Inject]
        public IFooterSettings Settings { get; set; }
    
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            filterContext.Controller.ViewData.Add("footerSettings", Settings);
        }
    }

    We’ve simplified this class quite a bit, since it no longer needs to know about the way footer settings are retrieved. This simplification is a core benefit of dependency injection as this class can now implement the single thing it’s supposed to do: provide the footer settings to ViewData.

    Attribute classes are instantiated directly by the .NET Framework using a parameterless constructor, so we can’t use constructor-based injection (which is the preferred method in most cases). Instead, we’re marking up the Settings property with Ninject.InjectAttribute so that Ninject knows we want something injected here. There are ways to use auto-wiring or our own attribute so we don’t have to take a dependency on Ninject throughout our classes, but we’re starting with the default use case here.

    Now that we’ve declared that we want the property injected, the Ninject kernel needs to know what to inject. This is done by adding a Ninject module and configuring some bindings. To do so, we’ll add a new class called WebModule under a new Components directory in the project.

    public class WebModule : NinjectModule
    {
        public override void Load()
        {
            Bind<IFooterSettings>()
                .ToProvider(new FooterSettingsProvider(WebConfigurationManager.AppSettings))
                .InSingletonScope();
    
            // AccountController injection
            Bind<IFormsAuthentication>()
                .To<FormsAuthenticationService>();
            Bind<IMembershipService>()
                .To<AccountMembershipService>();
            Bind<MembershipProvider>()
                .ToMethod(ctx => Membership.Provider);
        }
    }

    We’re also configuring bindings for AccountController‘s dependencies since Ninject’s default behavior is to use the constructor with the most arguments and the MVC project template includes constructors that allow you to pass its dependencies in. Should it be necessary, we could change the authentication and membership services used by the application just by altering these bindings.

    This module is then loaded into the kernel within the CreateKernel method of MvcApplication (Global.asax).

    protected override IKernel CreateKernel()
    {
        var modules = new INinjectModule[]
                    {
                        new WebModule()
                    };
    
        return new StandardKernel(modules);
    }

    All we’re doing here is telling Ninject that when it’s asked to inject an IFooterSettings object, it should use the FooterSettingsProvider (using AppSettings) to create the object. We’ve basically moved the instantiation logic that was embedded in the ProvideFooterSettingsAttribute to a class whose sole purpose is to configure how to instantiate objects. An additional benefit is that we can easily configure it to be a singleton, so that we’re not creating a new object for each request; after all, our AppSettings won’t be changing while the application is running.

    The code won’t build at the moment since Ninject’s ToProvider() method expects a Ninject.Activation.IProvider which our provider class doesn’t currently implement. Let’s fix that.

    public class FooterSettingsProvider : Provider<FooterSettings>
    {
        private static readonly string GoogleAnalyticsIdKey = "footer.analytics.id";
        private static readonly string CopyrightNoticeKey = "footer.copyright.template";
    
        public FooterSettingsProvider(NameValueCollection settings)
        {
            Settings = settings;
        }
    
        protected NameValueCollection Settings { get; set; }
    
        protected override FooterSettings CreateInstance(IContext context)
        {
            var analyticsId = Settings[GoogleAnalyticsIdKey] ?? string.Empty;
            var copyrightNotice = Settings[CopyrightNoticeKey] ?? string.Empty;
    
            return new FooterSettings(analyticsId, copyrightNotice);
        }
    }

    Changing FooterSettingsProvider to inherit from Ninject’s Provider<T> takes care of most of the work. All that’s left is to replace the  Create() method with Ninject’s CreateInstance(IContext context). Since we’re not doing any conditional instantiation, we just ignore the context and instantiate the object exactly as we did previously.

    Consider Yourself Injected

    Hypodermic needle; Photo by: stevendepolo (Flickr) The project will now build and execute with IFooterSettings being instantiated and injected by Ninject, along with AccountController’s dependencies. We’ve removed the dependencies in ProvideFooterSettingsAttribute, moving instantiation logic to a location specifically dedicated to such purposes. This allows us to have objects with a single responsibility, meaning they should only have one reason to change and thus be more maintainable over time.

    Ninject does a lot of stuff, and this post only touches upon the very basics, so if you’re interested in more about it, go check out the documentation. Since the current docs are aimed at Ninject 1.x, it may also be worth looking through the code or at least spelunking through the assembly with Reflector.

    This post was written using Visual Studio 2008, ASP.NET MVC v1.0, .NET 3.5 and code from the master branch of Ninject 2.0 (Pre-release) as of July 31, 2009. The source of the demo application is available for download.

    kick it on DotNetKicks.com

    PrintView Printer Friendly Version

    EmailEmail Article to Friend

    Reader Comments (6)

    Nice and informative post.

    Thank you for posting. Well, I was searching something about .NET and I found your article really interesting and kept on reading it.

    Thanks you mate!

    August 25, 2010 | Unregistered CommenterHouston Webdesign

    How would you use Ninject with Nunit?

    August 27, 2010 | Unregistered Commenterdustkicka

    @dustkicka

    One of the main reasons for adopting dependency injection into your application is to promote separation of concerns. This allows you to decouple objects from their dependencies and should make it simpler to test classes independently.

    In other words, I typically don't use IoC tools (like Ninject) in unit tests, but instead instantiate objects under test with stubs or mocks for dependencies. This allows me to test the single class I'm interested in without its dependencies, making it more likely that I'm building tests that are only concerned with the functionality being tested.

    For integration tests, it can be necessary to spin up Ninject, which is why it's good to keep your modules separate (i.e., have a module that loads only data-layer bindings) so that you can test the various layers of your application in separation.

    For actual examples, you may find some interesting stuff in the Ninject source, specifically in the Ninject.Tests project.

    August 27, 2010 | Registered CommenterKevin Rohrbaugh

    Nice post.
    It is useful and informative.
    Thanks for sharing such a great informative post....

    Magento Development

    October 22, 2010 | Unregistered CommenterMagento Development

    Nice creation.

    Thanks for information

    October 29, 2010 | Unregistered CommenterMagento Developer

    It is really good.

    Thanks

    October 30, 2010 | Unregistered CommenterMagento Developer
    Comments for this entry have been disabled. Additional comments may not be added to this entry at this time.