Legend
Legend

Reputation: 116900

Dynamically extending features of an application?

I recently came across a way to develop pluggable application modules when using ASP.NET MVC3/4 and I loved the simplicity of the approach. Currently, I have my applications structured as follows:

enter image description here

Therefore, anyone wanting to develop an extension for my application, follows the approach from the above tutorial and creates an extension that stays in the Areas folder. I figure that when new Areas (created as new projects) are added, .pdb files are created and placed in the bin directory. My question is this:

In my _Layout.cshtml (global shared layout), I do the following to construct the links:

<ul>
      <li>@Html.ActionLink("Area1", "Index", "Area1", new { Area = "Area1" }, null)</li>
      <li>@Html.ActionLink("Area2", "Index", "Area2", new { Area = "Area2" }, null)</li>
      <li>@Html.ActionLink("Area3", "Index", "Area3", new { Area = "Area3" }, null)</li>
</ul>

For simplicity, assume that the area names are unique. Any suggestions on how to do this?

Upvotes: 7

Views: 2050

Answers (5)

Akos Lukacs
Akos Lukacs

Reputation: 2047

How does one distribute the Areas as pluggable modules?

Don't create the Areas in the hosting web app, but create separate projects, that compile to separate dll's. Copy the dll's to any web app where you want to use it. Remember to set all static files as "EmbeddedResource".

How do I change the following code so that when someone drops a new Area into the bin folder, the application automatically picks it up and creates a link? And what should the plugin author do to enable this?

You can use MvcContrib PortableAreaRegistration's "Bus" to send messages, commands from the portable area to anyone on the 'bus'. That can be the hosting web app, or in theory independent Area's can send message to each other.

Created two crude, but functional demo of this, code on github:

MVC3 version:

https://github.com/AkosLukacs/PluggablePortableAreas

MVC4 RC version:

https://github.com/AkosLukacs/PluggablePortableAreasMVC4

First, you define a message that can carry the information you need. Just a POCO that has some properties (PluggablePortableAreas.Common\RegisterAreaMessage.cs):

public class RegisterAreaMessage : IEventMessage
{
    public string LinkText { get; private set; }
    public string AreaName { get; private set; }
    public string ControllerName { get; private set; }
    public string ActionName { get; private set; }
    //...
}

Create a handler for that message type (PluggablePortableAreas.Common\RegisterAreaEventHandler.cs):

public class RegisterAreaEventHandler : MessageHandler<RegisterAreaMessage>{}

In this case, the MessageHandler just adds the received messages to a static ConcurrentBag<RegisterAreaMessage>. You can use DI, if you want to, but wanted to keep it simple.

You can send the message from the portable area like this (Areas\DemoArea1\Areas\Demo1\Demo1AreaRegistration.cs):

 //the portable area sends a message to the 'bus'
 bus.Send(new RegisterAreaMessage("Link to the Demo area", AreaName, DefaultController, DefaultAction));

The dynamically added links are displayed by iterating over the collection of messages(PluggablePortableAreas.Web\Views\Shared_Layout.cshtml):

                @foreach(var sor in PluggablePortableAreas.Common.RegisterAreaEventHandler.RegisteredAreas) {
                <li>@Html.ActionLink(sor.LinkText, sor.ActionName, sor.ControllerName, new{Area=sor.AreaName}, null)</li>
                }

One more thing to take care of: Use "fully qualified" Area names. If you don't specify the area name explicitly, MVC assumes it's the current area. No problems without areas, but the second will point to "/path_to_your_app/CurrentArea/Home" instead of "/path_to_your_app/Home".

<li>@Html.ActionLink("Home", "Index", "Home", new { Area = "" }, null)</li>
<li>@Html.ActionLink("I don't work from the portable area!", "Index", "Home")</li>

Even one more thing to note!

The development server in VS feels a bit "erratic", sometimes the portable area doesn't load. Works reliably in full IIS tho...

Upvotes: 10

Wahid Bitar
Wahid Bitar

Reputation: 14094

take a look at this tutorial I think it'll help you a lot.

Upvotes: 1

Ventsyslav Raikov
Ventsyslav Raikov

Reputation: 7202

What you're doing looks very much alike the MVCContrib's Portable Areas.

They use the message/application bus pattern to dynamically add new widgets on web elements. MessageHandlers are discovered via reflection and each message is passed to all of them. So in your case the plugin author should just implement a handler for a standard message(say register global menu link).

Resources of the Portable Areas are embedded so just a single dll could be dropped in the bin folder. In order to automatically pick it up and use it in your app you will have to watch the bin folder say via FileSystemWatcher and restart your app(there is no other way to load the new .dll into the AppDomain in asp.net application I think).

You may load .dlls from other folders as well by using BuildManager functionality in ASP.NET 4. More useful info on that here.

Upvotes: 2

Sanja Melnichuk
Sanja Melnichuk

Reputation: 3505

Hi you can look for pretty nice plug in architecture in nopcommerce project.

Upvotes: 1

NeilD
NeilD

Reputation: 2298

This looks similar to how Orchard CMS does modules.

Take a look at their Gallery... The modules are distributed as Nuget packages, containing the whole module project.

Upvotes: 1

Related Questions