Click here to Skip to main content
15,175,179 members
Articles / Web Development / ASP.NET
Posted 1 Apr 2012


146 bookmarked

.NET 4.0 ASP.NET MVC 3 plug-in architecture with embedded views

Rate me:
Please Sign up or sign in to vote.
4.92/5 (60 votes)
8 Jul 2013CPOL5 min read
MVC 3 plugin architecture with embedded razor views: steb-by-step description and demo application.


This article demonstrates how to quickly build a plug-in architecture with ASP.NET MVC 3 and how to build new plugins for your application. It also shows how to create your views as embedded resources into the plugins and how .NET 4.0 features can help with discovering the new plug-ins from the host application.

Second Part shows how to add server side-logic inside plugins. 


I had to design a new ASP.NET MVC enterprise application that was marketed as software-as-a-service, basically being distributed to multiple customers by features. The customer can select which services he wants activate and pay only for those. Extra security of the code was also needed, all assemblies had to be signed and views have to be embedded for protection, so the best approach was to create a plug-in able architecture that could help accomplish all of this requirements and deliver extra features with ease. This also adds extra flexibility if custom requirements are needed from individual customers and allow them to create their own modules it they want.

Steps to create a plug-in system with embedded views 

Setup the plugin infrastructure

The host application has to identify all the plug-ins through a public library. A common interface is necessary for all plugins, that will be implemented by a class in the root of any plug-in. This interface must be located in a public assembly that will be referenced by all plug-ins and will contain common interfaces that are used as a bridge between a plug-in and the host application.

Let's call that interface the IModule interface:

public interface IModule
    /// <summary>
    /// Title of the plugin, can be used as a property to display on the user interface
    /// </summary>
    string Title { get; }

    /// <summary>
    /// Name of the plugin, should be an unique name
    /// </summary>
    string Name { get; }

    /// <summary>
    /// Version of the loaded plugin
    /// </summary>
    Version Version { get; }

    /// <summary>
    /// Entry controller name
    /// </summary>
    string EntryControllerName { get; }

The class that will implement this interface will carry all the information about name, version and default access to the plugin.

Plugin discovery

ASP.NET 4 introduces a few new extensibility APIs that are very useful. One of them is a new assembly attribute called PreApplicationStartMethodAttribute.

This new attribute allows you to have code run way early in the ASP.NET pipeline as the application starts up, even before Application_Start. This happens to be also before the code in your App_Code folder has been compiled. To use this attribute, create a class library and add this attribute as an assembly level attribute. Example:

[assembly: PreApplicationStartMethod(typeof(PluginTest.PluginManager.PreApplicationInit),"InitializePlugins")]

As seen above, a type and a string was specified. The string represents a method that needs to be a public static void method with no arguments. Now, any ASP.NET website that references this assembly will call the InitializePlugins method when the application is about to start, giving this method a chance to perform some early initialization.

public class PreApplicationInit
    /// <summary>
    /// Initialize method 
    /// </summary>
    public static void InitializePlugins()
    { ... } 

Now the real usage of this attribute is that you can add on run-time build providers or new assembly references, which in the previous versions of Visual Studio could be done only via web.confing. We will use this to add a reference to all the plug-ins before the application starts. Example:


The plug-ins should be copied in a directory inside the web application, but not referenced directly, instead a copy of them should be made and reference the copied plug-ins. All of this steps have to be made in the PreApplicationInit class static constructor.

public class PreApplicationInit
    static PreApplicationInit()
        string pluginsPath = HostingEnvironment.MapPath("~/plugins");
        string pluginsTempPath = HostingEnvironment.MapPath("~/plugins/temp");

        if (pluginsPath == null || pluginsTempPath == null)
            throw new DirectoryNotFoundException("plugins");

        PluginFolder = new DirectoryInfo(pluginsPath);
        TempPluginFolder = new DirectoryInfo(pluginsTempPath);
    /// <summary>
    /// The source plugin folder from which to copy from
    /// </summary>
    private static readonly DirectoryInfo PluginFolder;
    /// <summary>
    /// The folder to copy the plugin DLLs to use for running the application
    /// </summary>
    private static readonly DirectoryInfo TempPluginFolder;

    /// <summary>
    /// Initialize method that registers all plugins
    /// </summary>
    public static void InitializePlugins()
    { ... }

When the InitializePlugins method is called which will refresh the temp directory and reference all the plugin assemblies. When referencing, a check is required that validates the plugin by verifying if they contain a class that implements the IModule interface.

public static void InitializePlugins()

    //clear out plugins
    foreach (var f in TempPluginFolder.GetFiles("*.dll", SearchOption.AllDirectories))

    //copy files
    foreach (var plug in PluginFolder.GetFiles("*.dll", SearchOption.AllDirectories))
        var di = Directory.CreateDirectory(TempPluginFolder.FullName);
        File.Copy(plug.FullName, Path.Combine(di.FullName, plug.Name), true);

    //This will put the plugin assemblies in the 'Load' context
    var assemblies = TempPluginFolder.GetFiles("*.dll", SearchOption.AllDirectories)
            .Select(x => AssemblyName.GetAssemblyName(x.FullName))
            .Select(x => Assembly.Load(x.FullName));

    foreach (var assembly in assemblies)
        Type type = assembly.GetTypes()
                            .Where(t => t.GetInterface(typeof(IModule).Name) != null).FirstOrDefault();
        if (type != null)
            //Add the plugin as a reference to the application

            //Add the modules to the PluginManager to manage them later
            var module = (IModule)Activator.CreateInstance(type);
            PluginManager.Current.Modules.Add(module, assembly);

For this to work a probing folder must be configured in web.config to tell the AppDomain to also look for Assemblies/Types in the specified folders:

   <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <probing privatePath="plugins/temp" />

The singleton, PluginManager, is used to keep a track of all the plugins, for later use in the application.

In the final step, after the plugins are referenced it is necessary to register the embedded views from the plugins. This will be done using BoC.Web.Mvc.PrecompiledViews.ApplicationPartRegistry. I preferred to do it in a Bootstrapper class where I can, for example, also setup my DI container if I want. More information in the next chapter "Create embedded views". Bootstrapper overview:

public static class PluginBootstrapper
    public static void Initialize()
        foreach (var asmbl in PluginManager.Current.Modules.Values)

Steps to create a new plug-in project 

  1. Create new Class Library project.
  2. Create folders similar to an MVC application: Models, Views, Controllers. 
  3. Create  a module initializer class that implements the IModule interface: 
public class CalendarModule : IModule
    public string Title
        get { return "Calendar"; }
    public string Name
        get { return Assembly.GetAssembly(GetType()).GetName().Name; }
    public Version Version
        get { return new Version(1, 0, 0, 0); }
    public string EntryControllerName
        get { return "Calendar"; }

Create embedded views

The full tutorial, that I also followed, on how to add embedded Views to an MVC project can be found here. This chapter will only describe the absolute steps to this case. There are some simple steps to follow:

  1. Download the following Visual Studio Extension and install it from here.   
  2. The views have to be created like on a normal MVC project, in the same structure, in the Views directory. Then the views must be set as Embedded Resource and set the Custom Tool Namespace to MvcRazorClassGenerator.  
  3. After you add the Custom Tool some references will be added to the project, but some extra references are needed, realated to an MVC project. Make sure that all the references are added to the project, like: System.Web.Helpers, System.Web.WebPages, System.Web.MVC. 
  4. Last step is to make sure to register the embedded views: 

Using the code    

All needed assemblies are in the Binaries folder, on the same level with the solution file. You can find the following DLLs: BoC.Web.Mvc.PrecompiledViews.dll, Commons.Web.Mvc.PrecompiledViews.dll, System.Web.Helpers.dll, System.Web.WebPages.dll, System.Web.MVC.dll, System.Web.Razor.dll, System.Web.WebPages.Razor.dll.

All the plugins dlls are compiled in the directory PluginBin on the same root with the solution file.

The plugins are searched in the plugins directory inside the Web Application, the relative path from the solution file is PluginTest.Web/plugins

First make sure that the project is compiled and then copy the contents from PluginBin to PluginTest.Web/plugins

Make sure that the web-server is stopped or restart the web-server and run the application.

Feel free to play with the plugins by deleting one or more and then restart the application and see the results


Plug-in web applications can be easy achieved as described in this article, but web applications rarely needs this kind of added complexity. This is also only a proof of concept example of how this could be achieved and it does not represent a full blown implementation. The purpose of this article was to share a basic implementation that others could reuse and extend if required.


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


About the Author

Liviu Ignat
Software Developer (Senior)
Germany Germany
.NET & JavaScript Senior Software Lead Developer & Architect.
Since 2005 worked with most of .NET technologies (Windows Forms, ASP.NET, WCF, WPF, XNA) and for some years passionate about JavaScript and client JavaScript Architecture.

Comments and Discussions

QuestionNice article Pin
Damir Galas23-Aug-18 3:30
MemberDamir Galas23-Aug-18 3:30 
Member 1329664813-Jul-17 0:57
MemberMember 1329664813-Jul-17 0:57 
QuestionHow Does the MVC Framework Know where to find the Views from Pin
rickypatnaik24-Mar-17 2:15
Memberrickypatnaik24-Mar-17 2:15 
QuestionError regarding- Method not found: 'System.Collections.Generic.Dictionary.....plz help Pin
Member 1171927212-Feb-16 2:04
MemberMember 1171927212-Feb-16 2:04 
GeneralMy vote of 5 Pin
André Mansur25-Jun-15 3:07
MemberAndré Mansur25-Jun-15 3:07 
Questionso every time someone wants to add a new plugin he just have to stop the web server? Pin
angarato surion25-Feb-15 9:43
Memberangarato surion25-Feb-15 9:43 
QuestionI add a project but i can't find my cshtml file Pin
Member 106946853-Jan-15 21:36
MemberMember 106946853-Jan-15 21:36 
QuestionReflectionTypeLoadException Pin
Lord of Scripts6-Aug-14 8:06
MemberLord of Scripts6-Aug-14 8:06 
QuestionResource cannot be found Pin
Lord of Scripts5-Aug-14 14:30
MemberLord of Scripts5-Aug-14 14:30 
QuestionComparisons with areas in ASP.NET MVC Pin
TanTR15-Jun-14 18:03
MemberTanTR15-Jun-14 18:03 
QuestionI cannot develop a plugin using Framework 4.5, why? Pin
sayth16-Mar-14 17:19
Membersayth16-Mar-14 17:19 
Questionnew host build is always needed! Pin
HH from beirut4-Mar-14 2:36
MemberHH from beirut4-Mar-14 2:36 
AnswerRe: new host build is always needed! Pin
HH from beirut4-Mar-14 2:58
MemberHH from beirut4-Mar-14 2:58 
GeneralRe: new host build is always needed! Pin
Liviu Ignat4-Mar-14 13:11
MemberLiviu Ignat4-Mar-14 13:11 
GeneralRe: new host build is always needed! Pin
hady.elhaddad4-Mar-14 21:21
Memberhady.elhaddad4-Mar-14 21:21 
GeneralRe: new host build is always needed! Pin
Liviu Ignat4-Mar-14 22:48
MemberLiviu Ignat4-Mar-14 22:48 
GeneralRe: new host build is always needed! Pin
HH from beirut5-Mar-14 5:43
MemberHH from beirut5-Mar-14 5:43 
GeneralRe: new host build is always needed! Pin
V Navin Chandran7-Sep-15 2:33
professionalV Navin Chandran7-Sep-15 2:33 
GeneralRe: new host build is always needed! Pin
Liviu Ignat7-Sep-15 4:58
MemberLiviu Ignat7-Sep-15 4:58 
GeneralRe: new host build is always needed! Pin
V Navin Chandran8-Sep-15 20:52
professionalV Navin Chandran8-Sep-15 20:52 
GeneralRe: new host build is always needed! Pin
Liviu Ignat8-Sep-15 23:05
MemberLiviu Ignat8-Sep-15 23:05 
GeneralRe: new host build is always needed! Pin
V Navin Chandran1-May-16 2:39
professionalV Navin Chandran1-May-16 2:39 
GeneralRe: new host build is always needed! Pin
Damir Galas23-Aug-18 3:26
MemberDamir Galas23-Aug-18 3:26 
QuestionRole Based MVC Plug in architecture Pin
vishnu.vardhan4141-Dec-13 21:06
Membervishnu.vardhan4141-Dec-13 21:06 
QuestionYou've solved our problem Pin
Chris Maunder12-Jul-13 17:52
cofounderChris Maunder12-Jul-13 17:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.