Click here to Skip to main content
15,887,822 members
Articles / Web Development / ASP.NET

Build Asp.net Dependency Injection, IoC plugin base on Autofac

Rate me:
Please Sign up or sign in to vote.
4.88/5 (5 votes)
24 Mar 2013CPOL5 min read 20.7K   29  
Explains how to bring Autofac into Asp.net plugin framework

Introduction

There are quite a lot good articles about asp.net dependency injection and IoC, most solutions seems attractive but you have to develop everything almost from scratch.   

Fortunately, there are some elegant IoC frameworks kill the pain, like Autofac  and Spring.net. Autofac steps further to develop some libraries to integrate with Asp.net, which save much time for us.

So what's this article for? I'm not trying to invent new wheel, but to bring Ioc & DI features into Asp.net plugin framework by integrating Autofac, and this solution is also valid for integrating other libraries. 

Background  

I post an article Asp.net MVC plugin framework to show the solution of building modulized asp.net MVC plugin. Plugin framework oriented design supposed to be able to easily integrate third party libraries, that's what plugin framework for. so I decided to build an IoC plugin based on Autofac.  

Using the code

Before we go further, let's get start with developping a demo to use IoC plugin, the completed source code can be download here, please choose

OSGi.NET Integration with Asp.NET MVC 3 

or

OSGi.NET Integration with Asp.NET MVC 4

either is fine.

Let's take the MediaPlugin for instance, the requirement is load all popular movices from data storage (database or somewhere), then show on page. So I create a movice data access interface, its definition is following,

C#
public interface IMoviceManager
 {
     List<Movie> GetMovies();
 }

Secondly, I create a MediaManagement plugin, which is actually the data access layer, the mockup implementation is below, 

C#
public class MovieManager:IMoviceManager
    {
        private static List<Movie> _movies;
        static MovieManager()
        {
            _movies = new List<Movie>();
            _movies.Add(new Movie() { Name = "The Breaking Bad", Rating = 5 });
            _movies.Add(new Movie() { Name = "The Avatar", Rating = 5 });
            _movies.Add(new Movie() { Name = "The Walking Dead", Rating = 4 });
        }

        public List<Movie> GetMovies()
        {
            return _movies;
        }
    } 

In the plugin activator, I register the data access layer into Autofac like this, 

C#
public class Activator:IBundleActivator
    {
        public void Start(IBundleContext context)
        {
            var builder = context.GetFirstOrDefaultService<ContainerBuilder>();
            builder.RegisterType<MovieManager>().AsImplementedInterfaces();
        }

        public void Stop(IBundleContext context)
        {
        }
    }

The Activator in OSGi.NET is the entry of plugin. When plugin is active, the method Start is invoked, if changed to inactive, Stop is invoked, which enables user has chance to do something like prepare resource and release resource. The activator is optional, Click here  to learn a simple demo of activator.

For now, the data access plugin is ready, next step is to create a MediaPlugin to display movices on web page.

The page's controller definition is following,

C#
public class PopularTVShowController : Controller
   {
       private readonly IMoviceManager _moviceManager;

       /// <summary>
       /// Create instance of HelloController。
       /// </summary>
       /// <param name="moviceManager">MoviceManager business layer implementation, which is injected by IoC.</param>
       public PopularTVShowController(IMoviceManager moviceManager)
       {
           _moviceManager = moviceManager;
       }

       public ActionResult Index()
       {
           //
           return View(_moviceManager.GetMovies());
       }
   }

When accessing its view, the controller is auto constructed.  The running mode screen shoot is below,

 How  it works?  

To help us better understand the auto injection mechanism, I post the debugging screenshoot as below,

 

From the call stack you can see we customized a ControllerFactory in the plugin framework, which responses to create controller instance. Below are the main steps it works,

  1. Let's say user accesses a plugin page, the URL is http://localhost/MediaPlugin/PopularTVShow/Index; 
  2. Our customized ControllerFactory identify the plugin name is MediaPlugin and controller name is PopularTVShow from the URL ; 
  3. Customized ControllerFactory starts the MediaPlugin if it's inactive, then  resolves the Controller type from the plugin assemblies; 
  4. ControllerFactory tries to load ControllerResolver service to construct the controller instance. This is the key to IocPlugin, because the  ControllerResolver service is provided by IocPlugin. If the ControllerFactory can't find available ControllerResolver service, it calls System.Activator.CreateInstance instead. 

How  to create IoC plugin?

There is no trick here, simply create a empty plugin at first. When the plugin is active, construct Autofac ContainerBuilder, then register the instance into you plugin framework. The plugin framework in this project is supported by OSGi.NET, the registeration is below, 

C#
public static ContainerBuilder Initialize(this BundleRuntime runtime)
        {
            //provide the container builder so that each plugin can register the dependancy when starting.
            var containerBuilder = new ContainerBuilder();
            runtime.AddService(typeof(ContainerBuilder), containerBuilder);
            return containerBuilder;
        }

Then the only thing need to do is monitoring any plugin change, and maintain the assemblies for ContainerBuilder.  The piece of registering assembly code is following,

C#
public static void SafeRegisterControllers(this ContainerBuilder containerBuilder, Assembly[] assmblies)
      {
          //ContainerBuilder is not thread safe。
          lock (containerBuilder)
          {
              var container = BundleRuntime.Instance.GetFirstOrDefaultService<IContainer>();
              if (container == null)
              {
                  //If container is newly created,it can accept assmblies right now.
                  containerBuilder.RegisterControllers(assmblies);
              }
              else
              {
                  ContainerBuilder anotherBuilder = new ContainerBuilder();
                  anotherBuilder.RegisterControllers(assmblies);

                  anotherBuilder.Update(container);
              }
          }
      }

Points of Interest 

Aotufac does the real work

The IoC plugin I created here actually reuses Autofac integration library to handler controller dependency injection. The Autofac ContainerBuilder is not thread safe, so you have to lock it before updating.

The solution is plugin framework independent

This plugin framework is based on the ASP.NET MVC plugin framework, but not limited to that framework. This solution should work with all plugin frameworks, like MEF, Mono Addin, but you may need to implement the logic of monitoring plugin Start/Stop. 

Plugin Dependency  & Resolve

A good nature of plugin framework is any plugin can be removed/stop/start anytime. Let's look back the plugins we created, you will find MediaPlugin depends on IocPlugin, so MediaPlugin only works when IoCPlugin is active.  In real life, maybe for any reason, the IocPlugin is temporary unavailble,  in this case, all views in MediaPlugin should be invisible to end users, otherwise, users will see error page complaining "No parameterless constructor defined for the object". So we should explicitely tell the plugin framework that MediaPlugin depends on IoCPlugin, so once IocPlugin is unavailble, the IocPlugin isn't either, this is known as Dependency & Resolve in plugin framework.

In OSGi.NET, the dependency is defined in plugin manifest file as below,

XML
 <?xml version="1.0" encoding="utf-8"?>
<Bundle xmlns="urn:uiosp-bundle-manifest-2.0" Name="MediaPlugin" SymbolicName="MediaPlugin" Version="1.0.0.0" InitializedState="Active">
  <Activator Type="MediaPlugin.Activator" Policy="Immediate" />
  <Runtime>
    <Assembly Path="bin\MediaPlugin.dll" Share="false" />
    <Dependency BundleSymbolicName="UIShell.IoCPlugin" Resolution="Mandatory" />
  </Runtime>
</Bundle> 

 If you are using other plugin framework, you'd better take this scenario into consideration.

 

Sequence to Start Plugin

For enterprise application, there are often hundreds of plugins, so the order to start plugin is crucial, some core plugins like DataAccessPlugin, AuthenticationPlugin usually start earier than others. I suggest developer don't make their plugins rely on the start sequence. The IocPlugin isn't essential to most application, so there's no gurantee it always starts prior to others. Consider below secnario, 

  1. Plugin1 get started, its controllers depends on IocPlugin same as MediaPlugin does;
  2. IocPlugin started, it will monitor any plugin active/inactive;
  3. MediaPlugin started, so IocPlugin received the plugin active notification, then loads MediaPlugin assembly into the ContainerBuilder; 

Do you see what's wrong going here? The assemblies in Plugin1 are ignored by IocPlugin! A good design of IocPlugin should be able to deal with this case, which is load assemblies started prior to it.

Please developer remember the good practice: 

Don't make your plugins rely on start sequence as possible as you can.

Migrate more libraries  into plugins

Let's summarize the key steps of the migration,

  1. Create a empty plugin project with Activator class, add reference to third part assemblies;
  2. Construct your service in plugin Activator, and register it to plugin framework service container. For IocPlugin, we create ContainerBuilder, then put it to bundle service container by invoking
    C#
    BundleRuntime.AddService(typeof(ContainerBuilder), containerBuilder);

All other plugins can get the ContainerBuilder from the service container, and use it directly.

With this solution, we can migrate libraries into plugins as more as we need. For instance, we can 

  1. Implement a Message Broker plugin by integrating with ActiveMQ or ESB;
  2. Create Logging plugin by integrating log4net or enterprise library;
  3. others as you need. 

 

History  

None.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
China China
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --