Tallan's Technology Blog

Tallan's Top Technologists Share Their Thoughts on Today's Technology Challenges

Multi Tenant Architecture via Dependency Injection: Part 2

In Part 1 we discussed Multi Tenant Architecture as it pertains to web development, along with how dependency injection (specifically with Ninject) can allow us to manage our necessary customizations a bit cleaner.

To start off with, let’s talk about using Ninject with ASP.Net MVC. You’ll need to download the appropriate Ninject version for your .NET framework. Our example will use Ninject 2.0 with ASP.Net MVC 1. Download Ninject from the homepage at http://ninject.org/ and visit the dojo and github to learn the basics. Ninject will manage our controllers for us (it needs to, so it can monitor for injection points) and so will need to be tightly integrated into our application. For starters, we’re going to need to change our global.asax.cs file. Take a look:

Global.asax.cs:

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Ninject.Web.Mvc;
using Ninject;
using Ninject.Modules;
using MultiTenantNinject.Controllers.Components;
using MultiTenantNinject.Models;

namespace MultiTenantNinject
{
  // Inherit from NinjectHttpApplication instead of HttpApplication
  public class MvcApplication : NinjectHttpApplication
  {
    public static void RegisterRoutes(RouteCollection routes)
    {
      routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

      // hack for visual studio's web server
      routes.IgnoreRoute("{*favicon}", new { favicon = @"(.*/)?favicon.ico(/.*)?" });

      routes.MapRoute(
        "Default", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        new { controller = "Home", action = "Index", id = "" } // Parameter defaults
      );

    }

    // we use OnApplicationStarted, because Ninject is using Application_Started
    protected override void OnApplicationStarted()
    {
      // no big surprise here -- register our routes.
      RegisterRoutes(RouteTable.Routes);
      // register every controller in our application for injection
      RegisterAllControllersIn(Assembly.GetExecutingAssembly());
    }

    // this is called automatically from within the Ninject framework
    protected override IKernel CreateKernel()
    {
      // create a Kernel to manage injections.
      // have the Kernel process our "SetupModule" class
      IKernel k = new StandardKernel(new SetupModule());
      // have the Kernel process our "ConfigModule" class
      k.Load(new ConfigModule());
      // have the Kernel process our "OrderModule class
      // pass an instance of our company config that our ConfigModule calls for
      k.Load(new OrderModule(k.Get<ICompanyConfig>()));
      return k;
    }
  }
}

You can see how the CreateKernel() method loads a number of Modules to manage our Dependency Injection. Let’s take a look at these modules. The first module to investigate will provide bindings for our .NET MVC dependencies.

SetupModule.cs:

using {etc}
using Ninject;
using Ninject.Modules;
using Ninject.Web.Mvc;

namespace MultiTenantNinject.Controllers.Components
{
    // inherit from NinjectModule
    public class SetupModule : NinjectModule
    {
        // NinjectModule is an implementation of the Strategy design pattern and must implement "Load()"
        public override void Load()
        {
            // Let's inject .NET MVC framework dependencies, wow!
            Bind<IFormsAuthentication>()
                .To<FormsAuthenticationService>();
            Bind<IMembershipService>()
                .To<AccountMembershipService>();
            Bind<MembershipProvider>()
                .ToMethod(ctx => Membership.Provider);
        }
    }
}

Our next module will bind the heart of our Multi Tenant Architecture, the “Company Config”. This configuration object will be lightweight, cached server-side (ideally once for each company) and will be available during most operations of the page lifecycle.

ICompanyConfig.cs:

using {etc}

namespace MultiTenantNinject.Models
{
    public interface ICompanyConfig
    {
        // Task #1 -- provide service objects
        Dictionary<string, string> Bindings { get; set; }
        // Task #2 & #3 -- provide views and partial views
        Dictionary<string, string> Views { get; set; }

        String CompanyName { get; set; }

    }
}

ConfigModule.cs:

using {etc}

namespace MultiTenantNinject.Controllers.Components
{
    public class ConfigModule : NinjectModule
    {
        public override void Load()
        {
            // Ninject.Activation.Provider is a factory object.
            CompanyConfigProvider Provider = new CompanyConfigProvider(WebConfigurationManager.AppSettings);

            // Bind our service (ICompanyConfig) to its implementation using the
            // aforementioned Provider.  Bind it so that each request uses the same
            // instance of the service.
            Bind<ICompanyConfig>()
                .ToProvider(Provider)
                .InRequestScope();

        }
    }
}

Our next module will control the functionality for our example customizations revolving around Purchase Orders. In practice we could have multiple modules, perhaps one for each functional area of our application, but our single module will serve our purposes here:

OrderModule.cs:

using {etc}

namespace MultiTenantNinject.Controllers.Components
{
    public class OrderModule : NinjectModule
    {
        private readonly ICompanyConfig _config;

        // We see our first "Inject" attribute.
        // Here we will take an instance of our company config
        // and later use it to bind our application services
        [Inject]
        public OrderModule(ICompanyConfig companyConfig)
        {
            _config = companyConfig;
        }

        public override void Load()
        {
            // use reflection to bind a list of types (from our Config's "Bindings" collection)
            // to be used when called upon during injection
            string serviceLocationPrefix = "MultiTenantNinject.Services.";
            foreach (KeyValuePair<string,string> item in _config.Bindings)
            {
                Bind(Type.GetType(serviceLocationPrefix + item.Key)).To(Type.GetType(serviceLocationPrefix + item.Value));
            }
        }
    }
}

Let’s briefly revisit our goals from Part #1:

  1. dynamically set which service object(s) we are using to perform our application logic
  2. dynamically set which view we are returning
  3. dynamically set which partial views we are using

At this point we can see the pattern unfolding. We use our Modules to dictate to the Kernel what strategies to use when binding implementations to our services. We can use reflection, we can use strings and types, and we can use Providers inside of our Modules during binding. We will be coming back to OrderModule.cs, as that is part of our implementation for Goal #1. Let us take a look now at our Provider from our ConfigModule:

CompanyConfigProvider.cs:

using {etc}
using Ninject.Activation;

namespace MultiTenantNinject.Models
{
    // inherit from Ninject.Activation.Provider
    public class CompanyConfigProvider: Provider<CompanyConfig>
    {
        // these settings are provided by our web.config in this example
        public CompanyConfigProvider(NameValueCollection settings)
        {
            Settings = settings;
        }

        protected NameValueCollection Settings { get; set; }

        // build and return an instance of our CompanyConfig using the specific context
        // IProvider is a Factory that will have its CreateInstance() method called
        // the first time that a CompanyConfig is injected
        protected override CompanyConfig CreateInstance(IContext context)
        {
            // use a caching mechanism to store our company config.  we cannot
            // cache in the Application cache because we have a Multi Tenant Application.
            // we can use Session in a pinch, although depending on your application's traffic,
            // you might need to use something else
            System.Web.SessionState.HttpSessionState Session = HttpContext.Current.Session;
            CompanyConfig cc = null;

            // if we don't have a cached company config:
            if (Session == null || Session["cc"] == null)
            {
                cc = new CompanyConfig();
                // our repository is extremely unsophisticated, but you should
                // get the idea of how this works.  it may very well be passed in
                // via constructor injection
                MultiTenantNinject.Models.Repository r = new Repository();

                // construct our list of Views to be used throughout the application
                // for Goals #2 and #3
                foreach (KeyValuePair<string, string> item in r.getViews())
                {
                    cc.Views.Add(item.Key, item.Value);
                }
                // construct our list of service bindings to be used throughout the application
                // for Goal #1 (previously seen in OrderModule.cs
                foreach (KeyValuePair<string, string> item in r.getBindings())
                {
                    cc.Bindings.Add(item.Key, item.Value);
                }
                // if we can cache our company config:
                if (Session != null && Session["cc"] == null)
                {
                    Session["cc"] = cc;
                }
                // set company name (normally would be from the repository)
                cc.CompanyName = "Widgets, Inc.";
            }
            else  // we have a previously cached company config
            {
                cc = (CompanyConfig)Session["cc"];
            }

            return cc;
        }
    }
}

To make our lives a little easier and yet more structured, let’s create an ActionFilterAttribute to inject our CompanyConfig into our Views:

From ProvideCompanyConfigAttribute:

    public class ProvideCompanyConfigAttribute : ActionFilterAttribute
    {
        [Inject]
        public ICompanyConfig Settings { get; set; }

        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            filterContext.Controller.ViewData.Add("companyConfigSettings", Settings);
        }
    }

We use Ninject’s “Inject” attribute to provide the CompanyConfig to our Attribute, and then inject the config into the controller as a matter of course.

At this point we’ve seen how Ninject positions itself within our application (a controller factory and application superclass) and how we can use Ninject’s modularity to manage our bindings. We’ve touched upon Goals #1, 2, and 3, and can see it taking shape before us. We have a company configuration that encapsulates the customizations for the currently logged in user and provides the customized bindings and views for that logged in user. We have basically everything we need to begin working with our customized functionality.

To get this far we have made the following assumptions:

  1. Your repository works.
  2. You have an ASP.Net MVC application in otherwise working order.
  3. You have downloaded and referenced the Ninject libraries.

In Part 3 we will look at our application controllers and views, and then explore what we can do to provide customized functionality to our users.

6 Comments. Leave new

Very interested in these blog posts and will be following closely, this is just what I am about to get into on an existing project I have….the only difference being is that my project is currently using Castle Windsor.

I’m hoping that the same process can be done using Castle instead of Ninject…if not i’m hoping I can both Castle + Ninject side-by-side.

Paul

Paul,

You can definitely wire Castle Windsor / MicroKernel into ASP.Net MVC and use it to provide dependency injection and a controller factory. As long as you have that at your disposal you should be able to create a framework to accomplish Goals 1, 2, and 3. The only sticking point, in my mind, would be the late bound dependency injection done with reflection in OrderModule.Load(). Good luck, and feel free to share your results!

This is an excellent post. Can I implement this with Unity ver. 2 of EntLib?
Thanks.

Moinak,

You’re all set if you want to use Unity to provide your DI within your ASP.Net MVC application. Unity has your controller factory built in, and you will just need to manage your bindings to provide the client functionality you desire. I’m not aware if Unity can provide dependency bindings via reflection — but if it can, then you’re all set.

Do you have this code as a downloadable working sample?

Sorry, I don’t have this code as a downloadable working sample. I had it, but lost it in a HD crash.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>