Pluggable modules for ASP.NET


When you design a modular ASP.NET application, soon or later you will need to think about adding extensibility features to your project so that it will be possible to add new modules at runtime.  There are a few architectures and designs that let you develop an extensible application, like ASP.NET MVP. However, many of them add a lot of complexity almost to everything and one should learn many concepts to use them.  Therefore, it’s a good idea to use other simple but innovative methods like the one I will explain bellow.

The method I am going to mention lets you develop an ASP.NET application and add some more modules to it later at runtime. In a nutshell, it has the following benefits:

  1. Allows adding new pages to an existing web application at runtime and does not need any recompilation.
  2. Allows adding new web parts to an existing content management system (Portal) at run-time.
  3. Several developers can develop different parts of an application.
  4. It is very easy to understand, develop and use.
  5. Does not exploit any 3rd party library so nothing is needed except Visual Studio.

And it has the following drawbacks:

  1. One should know the exact structure of an existing ASP.NET application, like folder hierarchies.
  2. May not cover all possible scenarios (Actually I have not taught about many scenarios).

How to implement it?

The design I am going to explain is possible only if you develop an ASP.NET Web Project rather than an ASP.NET web site.  As far as I remember, visual studio 2005 does not let you create a web project. If I am right, you need to use Visual Studio 2008. However, there are two main parts that we need to develop:

  • A web application project that includes main modules, main pages, and loads plugged modules, checks licensing, perform security tasks etc.
  • Plugged modules, which will add more pages, web parts and functionalities.

Main application and modules must match. It means that they must have same structure (i.e. folders), use same master pages and follow same rules.

The main reason that I used a Web Application Project, rather than a Web Site, was the benefits of a Web Application Project for developing a plug-in based web site. After building a web application project, there will be one assembly and several .aspx, .ascx, .ashx … files. After the web application is published, there is possibility to add more pages and files to it. Therefore, if at a later time we add several .aspx pages along with their .dll files, the web application will be able to work with those pages with no problem.

When developing the main application, you should consider a well formed directory structure, language specific contents, master pages etc. For example, your application should have a master page with a general name, like Site.Master. It also needs to maintain each module’s pages in a separate folder so that new modules can follow the same rule and avoid naming conflicts etc.

To develop the main application, follow the steps bellow:

  1. Create an empty solution in VS 2008.
  2. Add a new ASP.NET Web Project (not a web site) to the solution.
  3. Add any required folders like App_Themes and implement any required authentication, authorization and personalization mechanisms. Your web application must be complete and working.
  4. Add a master page to the web application project and name it Site.Master or another general name.
  5. Add a new Class Library Project and call it Framework (i.e. mycompany.myproject.Framework), common or whatever name that indicates this class library will be shared between the main application and dynamic modules.
  6. Add a new interface to the mentioned class library and call it IModuleInfo. This interface will be implemented with a class inside any pluggable module and will return root menu items that must be added to main application’s menu (or items to be added to a site navigation). It also can return a list of WebParts that introduces web parts that exist inside the module.

public interface IModuleInfo

{

List<MenuItem> GetRootMenuItems(string[] UserRoles);

}

UserRoles parameter is not mandatory. It shows that you can pass parameters to the method that returns a module’s main menu items. In this example, it indicates which Roles the current user has so that menu items will be filtered properly.

  1. Add a new ASP.NET Web Application project to the solution and name it SampleModule.
  2. Add a folder called SampleModule and if necessary, add more sub-folders.
  3. Add a web.config file to SampleModule folder and define which users/roles can access which folder.
  4. Add a master page named Site.Master. In fact , it must have same name with your master page in the main application.
  5. Add a public class with any name (I call it ModulePresenter) that implements IModuleInfo (this interface was added to Common or Framework library).

ModulePresnter class will return a list me menu items to main application. Main application will add those menu items as root items to its main menu afterwards. I will not bring a detailed code for the part that a module creates these items; it is dependent on your project.

public class ModulePresenter : IModuleInfo

{

#region IModuleInfo Members

public List<System.Web.UI.WebControls.MenuItem> GetRootMenuItems(string[] UserRoles)

{

List<MenuItem> items = new List<MenuItem>();

//:

//:

return items;

}

#endregion

}

  1. Compile this application and go back to the main application.
  2. Add an XML file and call it PluggedModules.xml. This file maintains the qualified type name of each module that must be loaded. A qualified type name includes assembly, namespace and class name

<?xml version=”1.0″ encoding=”utf-8″ ?>

<modules>

<module name=”SampleModule” type=” SampleModule.ModulePresenter, SampleModule.dll”></module>

</modules>

  1. Write a code to query PluggbedModules.xml, get menu items and attach them to main menu:

public static void LoadModules(Menu menuControl , string[] userRoles, string xmlName)

{

XDocument document = XDocument.Load(HttpContext.Current.Server.MapPath(string.Format(“~/{0}” , xmlName)));

var allModules = document.Elements(“modules”);

foreach(XElement module in allModules.Elements())

{

string type = module.Attribute(“type”).Value;

IModuleInfo moduleInfo = Activator.CreateInstance(Type.GetType(type)) as IModuleInfo;

List<MenuItem> allItems = moduleInfo.GetRootMenuItems(userRoles);

foreach(MenuItem item in allItems)

{

menuControl.Items.Add(item);

}

}

}

As seen in the above code, we query PluggedModule.xml file , extract introduced files and create an instance of it using Activator.CreateInstance method. Then extract IModuleInfo implementation, call GetRootMenuItems to get module’s menu items and add it to main menu.

After doing all the above steps, copy modules .dll file (generated after you build the project) to main application’s \bin folder and add it’s main folder (SampleModule) to main application’s root folder. It will work fine until all naming matches (for example both use master pages with a same name) and when specifying target URL in menu items, they point to a relative path, i.e. SampleModule/MyPage.aspx.

Please download the sample code from here.

Advertisements

14 thoughts on “Pluggable modules for ASP.NET

  1. Hoi Aref, it is I your former (though briefly) TLX colleague. I am working on my website (ASP.NET) and wanted to use your pluggable module thing. The document you prepared at TLX was more detailed.

    Anyway, this post was sufficient and I am giving it a major overhaul, very exciting! will let you know when I am done. Some years ago I wrote a plugin system based on controls rather than pages, worked like a charm too. Will integrate it into the final as well if time permits.

    • Aref Karimi says:

      That’s awesome. Let me know when you are done. I an architecting a system that needs extensibility so I might have to work on new techniques 🙂

  2. shohdi says:

    Thank you for that good easy structure , I have a time now searching for a good solution for pluggable modules on internet.

    thanks

  3. Hello Aref, thank you for the good article. I’m using a similar approach for modular website in my company and was looking for someone using the same architecture. It was mostly to validate my solution because, even though it is running fine in production for three years now, it was intriguing that I didn’t saw anyone else using the same approach for such a common need. It’s good to find someone else suggesting the same architecture and I can corroborate your article saying that it works great.
    I understand that with MVC we have more possibilities to accomplish the same task, but for a company like us that already has it’s products built with conventional ASP.Net and new modules being developed all the time, it may be difficult to migrate to MVC.
    The IModuleInfo interface that you suggested is the perfect solution for the menu. I will include that in my solution!

    Regards,
    Marcello Yesca

  4. ONeill says:

    Just a heads up for people using VS 2012 – When you type out the assembly name where the type is declared in the xml file, do not add the “.dll” to the end of the assembly name. (I don’t know if the sample code has this correct or not. I’m at work and our firewall wouldn’t let me check it.)

    I fought with a null exception for a long time before I figured that was the problem.

    Thank you for this article. It was a huge help.

  5. Your personal posting, “Pluggable modules for ASP.NET | Aspguy’s Weblog” victoria-fan ended up being very well worth commenting on! Simply just wished to mention u really did a tremendous work. Thanks for your time -Genia

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s