My attempt to use Composite Web Application Block with ASP.Net MVC Application
By ganton ~ November 20th, 2008. Filed under: ASP.Net.
Let’s say that there is an application written in ASP.Net MVC and that there is a need to make it composite. I’ve searched the Interenet and I didn’t find so much about how to do it. For sure using Web Client Software Factory (WCSF) it can be done but it is not suitable for my case. And then i found an article by David Hayden. This gave me an idea to try to do this my self and to see what will have on the end.
Here I’d like to present you the standard ASP.Net MVC application which is generated when you create an ASP.Net MVC application using Visual Studio. I’ve just reorganized it a bit in order to add into it Composite Web Application Block (CWAB). Let me say that this block is the core of the WCSF and it is created in order to cover its needs but using it as an infrastructure I think we are able to create an ASP.Net MVC application composite.
You can download the source code from here.
The first what I’ve did was to create a new MVC application and to add Microsoft.Practices.CompositeWeb.dll, Microsoft.Practices.CompositeWeb.EnterpriseLibrary.dll, Microsoft.Practices.ObjectBuilder.dll into its root folder (in a separate directory :)). I add three additional libraries into my solution Utils, Shell and HelpModule. It is depicted at the picture below.
The Utils library is not used as a business or foundational module but it contains class used in my application to register my own information about modules. ModuleMenuInfo class is an info class which contains module information that I use in web application.
public class ModuleMenuInfo
{
public string Key { get; set; }
public string Title { get; set; }
public string Controller { get; set; }
public string Action { get; set; }
}
The second class is ModuleMenuManager which manages module information. It allows adding, removing a module and retrieving all available modules.
public class ModuleMenuManager : IModuleMenuManager
{
private IDictionary<string, ModuleMenuInfo> _modules;
public ModuleMenuManager()
{
_modules = new Dictionary<string, ModuleMenuInfo>();
}
#region IModuleManager Members
public void AddModule(ModuleMenuInfo moduleInfo)
{
// code ...
}
public void RemoveModule(ModuleMenuInfo moduleInfo)
{
code ...
}
public IEnumerable<ModuleMenuInfo> GetModules()
{
return _modules.Values;
}
#endregion
}
Then I’ve created a module named Shell which is a required module for the application at whole. All additional modules depend on it. Normally, it should contain registration of any global services and other configuration operations. In my case I use it only for registering ModuleMenuManager class as a global service in order to use it in other modules for registering their selves. in addition, It registers itself to ModuleMenuManager because I need to display initial information in the shell. In a standard WCSF application it is a functional module and it doesn’t have any connection with the web site. In my case it is the same but it just adds some information used by presentation layer.
public class ShellModuleInitializer : ModuleInitializer
{
private IModuleMenuManager _moduleMenuManager;
public override void Load(CompositionContainer container)
{
base.Load(container);
AddGlobalServices(container.Parent.Services);
RegisterSiteMapInformation();
}
protected virtual void AddGlobalServices(IServiceCollection serviceCollection)
{
_moduleMenuManager = new ModuleMenuManager();
serviceCollection.Add<IModuleMenuManager>(_moduleMenuManager);
}
protected virtual void RegisterSiteMapInformation()
{
_moduleMenuManager.AddModule(new ModuleMenuInfo()
{
Key = "Shell",
Title = "Home",
Controller = "Shell",
Action = "Index"
});
}
}
As you can see ShellModuleInitializer inherits from ModuleInitializer class. It is a class from CWAB and each module should implement a class which inherits from him in order to register its services, types or whatever it needs to do at the time of module initialization.
The second module that I’ve created is a business module named HelpModule. It doesn’t contain any special functionality but a simple class named HelpLogic which only returns a method that returns a title name.
public class HelpLogic
{
public string GetTitle()
{
return "About Page";
}
}
It also has its own HelpModuleInitializer class where it retrieves an instance of IModuleMenuManager and use it in order to register its self.
public override void Load(CompositionContainer container)
{
base.Load(container);
RegisterSiteMapInformation(container.Parent.Services.Get<IModuleMenuManager>(true));
}
Finally, modules of my composite MVC application are ready for use and I can change the standard MVC application in the way that it will behave as a composite application. First of all I went to global.asax.cs file and change it as it is shown below.
public class MvcApplication : WebClientApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Shell", action = "Index", id = "" } // Parameter defaults
);
}
protected override void Start()
{
base.Start();
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(new CompositionControlerFactory());
base.AddRequiredServices();
}
}
I’ve change it to inherit from WebClientApplication file which is a class from CWAB which inherits from HttpApplication and extends it. Further, I delete Application_Start method and override Start method of WebClientApplication. It is executed only once when the application is started. There I registered the routes and I set my custom controller factory. AddRequiredServices of WebClientApplication registers all missed services used of CWAB.
CompositionControlerFactory class I need for executing WebClientApplication.BuildItemWithCurrentContext in order to build up the controller class requested and it will use ObjectBuilder to inject all dependencies a controller needs.
public class CompositionControlerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(Type controllerType)
{
if (controllerType != null)
{
object controller = Activator.CreateInstance(controllerType);
WebClientApplication.BuildItemWithCurrentContext(controller);
return controller as IController;
}
return null;
}
}
Further, I needed a BaseController class for getting all registered modules information and put it into ViewData. You can note that it has a property which holds an instance of IModuleMenuManager and that this property is decorated with ServiceDependency attribute from CWAB. Re-writing GetControllerInstance of DefaultControllerFactory I call WebClientApplication.BuildItemWithCurrentContext which will build up and inject needed class. But here I use ServiceDependency which tells CWAB that I need a service. Here I’ll get into ModuleMenuManager property a singleton of target class because I registered it as a global service and because I use ServiceDependency attribute. If I needed an unique instance for my controller I would use CreateNew attribute instead of ServiceDependency attribute.
[HandleError]
public class BaseController : Controller
{
[ServiceDependency]
public IModuleMenuManager ModuleMenuManager { get; set; }
protected void PresetMenuInfo()
{
foreach (var item in ModuleMenuManager.GetModules())
{
ViewData[item.Key + "_module"] = item;
}
}
}
I also created a HelpController class which is connected with HelpModule business module. Note that here I use CreateNew attribute in order to tell CWAB to build up for me a new instance of HelpLogic class each time I create HelpController.
public class HelpController : BaseController
{
[CreateNew]
public HelpLogic HelpLogic { get; set; }
public ActionResult About()
{
base.PresetMenuInfo();
ViewData["Title"] = HelpLogic.GetTitle();
return View();
}
}
Finally, I changed a bit the master page of the application in order to analyze the information from ViewData and according to it to add and remove menus.
<ul id="menu">
<%
if (ViewData["Shell_module"] != null)
{
Utils.ModuleMenuInfo shellModule = (ViewData["Shell_module"] as Utils.ModuleMenuInfo);
%>
<li><%= Html.ActionLink(shellModule.Title, shellModule.Action, shellModule.Controller)%></li>
<%
}
if (ViewData["Help_module"] != null)
{
Utils.ModuleMenuInfo helpModule = (ViewData["Help_module"] as Utils.ModuleMenuInfo);
%>
<li><%= Html.ActionLink(helpModule.Title, helpModule.Action, helpModule.Controller)%></li>
<%
}
%>
</ul>
Oh, I almost forgot to share that I also change Web.config file of the application where I’ve added information that CWAB uses to register my Shel foundational module.
<compositeWeb> <modules> <module name="Shell" assemblyName="Shell" virtualPath="~/"/> </modules> </compositeWeb>
I’ve also changed the Web.config file which is placed into Views folder. There I’ve added information about HelpModule business module. Note that it has a tag dependencies where one can list all modules the current depends on.
<compositeWeb> <modules> <module name="HelpModule" assemblyName="HelpModule" virtualPath="~/Views/Help"> <dependencies> <dependency module="Shell" /> </dependencies> </module> </modules> </compositeWeb>
I hope that you note virtualPath attribute in both XML snippets. It is not used in any case in this realization but it is used by CWAB and without it it will not work properly.
And now let me show two screens one when HelpModule module is registered via Web.config and one when it doesn’t registered. Below is the first snapshot where you can see two menus home and about.
And here is the second snapshot with only one menu attached because HelpModule module is not registered in the configuration file.


