Writing an Add-in architecture for your project these days is simple, using the right tools, but .NET suffers from a drawback, in that, you cannot unload an assembly once it is loaded into your AppDomain.
So, does this mean you cannot update an add-in without restarting your web-application?
There is a loop-hole, you can load two copies of the assembly into the same AppDomain.
Now you can repeat steps 5 and 6 as much as you want.
The drawback of course is that you are loading more and more stuff into the AppDomain, eak! Well actually, I am clearing down the Bin every time the Worker Process recycles - which is going to happen on occasion, and this is not something I'm going to be doing very often.
This is an ASP.NET MVC 3 Razor application, and I have created an implementation of IDependencyResolver using Ninject, this is where I have put my code for the module management.
This function is called at the start of the application from the HttpApplication.Application_Start() and passes the location for the storage of all this module monkey business.
protected void Application_Start() {
DependencyResolver.SetResolver(new ApplicationDependencyResolver());
ApplicationDependencyResolver.InitModules(Server.MapPath("~/App_Data/"));
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
private static string _modulesCachePath;
private static string _modulesBinPath;
internal static void InitModules(string storeRootPath) {
_modulesCachePath = Path.Combine(storeRootPath, "ModulesCache");
_modulesBinPath = Path.Combine(storeRootPath, "ModulesBin");
if (Directory.Exists(_modulesBinPath)) {
// clear down if exists
try { Directory.Delete(_modulesBinPath, true); }
catch (IOException) { } // ignore expected error
}
// create storage
if (!Directory.Exists(_modulesBinPath))
Directory.CreateDirectory(_modulesBinPath);
if (!Directory.Exists(_modulesCachePath))
Directory.CreateDirectory(_modulesCachePath);
foreach (var filePath in Directory
.GetFiles(_modulesCachePath, "*.cachedDll")) {
LoadModule(filePath);
}
}This function is called with the path to a cached dll and is responsible for loading up the implementation into the AppDomain and binding the modules for use by the application.
internal static void LoadModule(string cacheImplementationPath) {
// copy the file to the modules bin, with a random name
// this allows you to update as many times as you want
var binImplementationPath = Path.Combine(
_modulesBinPath, Guid.NewGuid().ToString("N") + ".dll");
System.IO.File.Copy(cacheImplementationPath, binImplementationPath);
// load the assembly
var updatedImplementation = Assembly.LoadFile(binImplementationPath);
// bind for use
((ApplicationDependencyResolver)DependencyResolver.Current).Container
.Rebind<ISaySomethingModule>()
.To(updatedImplementation.GetTypes()
.First(t => typeof(ISaySomethingModule).IsAssignableFrom(t)));
}This function is called to update the cached implementation with a new one.
internal static void UpdateModule(Stream fileStream, string moduleName) {
// copy the file to the modules cache
// only allow for one implementation per moduleName
var singleImplementationPath = Path.Combine(
_modulesCachePath, moduleName + ".cachedDll");
using (var file = System.IO.File
.Create(singleImplementationPath)) {
fileStream.CopyTo(file);
}
LoadModule(singleImplementationPath);
}I have a controller with two actions;
Index: Shows the result of the call to ISaySomethingModule.Say() using the current implementation, or if none exists, a default
Upload: Accepts a new assembly to update the implementation of ISaySomethingModule
Starting the application you can see the default message as there is no Implementation to call on.

So I browse to, and upload the "Wise" implementation.

This gives me a wise saying...

The Cache has been updated with the new assembly (safely named)

And the Bin contains the loaded version

Now I update again with the other implementation

The cache remains the same as above, but the bin now has a new assembly file which is loaded into the AppDomain

And after recycling the application this is cleared down to contain just the currently loaded implementation.

Happy? Well I am.
UpdateableAddin.zip VS2010 ASP.NET MVC Razor
Thanks for the attachement but what FF version do you use? I have a different view a bit.
| < | May 2012 | |||||
|---|---|---|---|---|---|---|
| S | M | T | W | T | F | S |
| 29 | 30 | 1 | 2 | 3 | 4 | 5 |
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 | 31 | 1 | 2 |
Add-ins AJAX ASP.NET MVC Browsers C# Caching CodeDom Compression CORS CSS CV Data Database DependencyResolver Development Dynamic Entity Framework Error Handling Extend File Upload Forms GDI+ HTML HTML Editor HTTP Interfaces JavaScript JQuery MCE MetadataProvider MSBuild Numbers Objects Patterns Progressive Enhancement Projects Publish Regex Resources Security SEO SMTP Source Control Strings Sub-Collections TDD Templates Tools Twitter User Interface WCF Web Development WHS WMC XLinq XML
One hour ago
TheNextWeb
Bing's search API now live on the Windows Azure Marketplace http://t.co/utX8uOuG by @alex
15/05/2012
WindowsAzure
Announcing the MEET Windows Azure Event! Streamed online June 7th. Register at http://t.co/bObzTAuL #MEETAzure #WindowsAzure
40 minutes ago
commadelimited
Buy the @amazon Kindle version of mine and @cfjedimaster's @jquerymobile book for $10 today: http://t.co/PWRZ2dkd
just now
achirinos
RT @CsharpCorner: "Hello World" Windows Phone Application Using PhoneGap or Cordova: In this article, we will… http://t.co/xAqgMDvN
just now
CsharpCorner
Autoscaling Application Blocks WIndows Azure: Autoscaling Application Blocks can… http://t.co/ryDpLWRE