I asked this on the MEF Codeplex forum already, but I haven't gotten a response yet, so I figured I'd try StackOverflow. Here's the original post if anyone's interested (this is just a copy from it):
MEF Codeplex
"Let me first say that I'm completely new to MEF (just discovered it today) and am very happy with it so far. However, I've ran in to a problem that is very frustrating. I'm creating an app that will have a plugin architecture and the plugins will only be stored in a single DLL file (or coded into the main app). The DLL file needs to be able to be recompiled during run-time and the app should recognize this and re-load the plugins (I know this is difficult, but it's a requirement). To accomplish this I took the approach covered http://blog.maartenballiauw.be/category/MEF.aspx there (look for WebServerDirectoryCatalog). Basically the idea is to "monitor the plugins folder, copy the new/modified assemblies to the web application’s /bin folder and instruct MEF to load its exports from there." This is my code, which is probably not the correct way to do it but it's what I found in some samples around the net:
main()...
string myExecName = Assembly.GetExecutingAssembly().Location;
string myPath = System.IO.Path.GetDirectoryName(myExecName);
catalog = new AggregateCatalog();
pluginCatalog = new MyDirectoryCatalog(myPath + @"/Plugins");
catalog.Catalogs.Add(pluginCatalog);
exportContainer = new CompositionContainer(catalog);
CompositionBatch compBatch = new CompositionBatch();
compBatch.AddPart(this);
compBatch.AddPart(catalog);
exportContainer.Compose(compBatch);
and
private FileSystemWatcher fileSystemWatcher;
public DirectoryCatalog directoryCatalog;
private string path;
private string extension;
public MyDirectoryCatalog(string path)
{
Initialize(path, "*.dll", "*.dll");
}
private void Initialize(string path, string extension, string modulePattern)
{
this.path = path;
this.extension = extension;
fileSystemWatcher = new FileSystemWatcher(path, modulePattern);
fileSystemWatcher.Changed += new FileSystemEventHandler(fileSystemWatcher_Changed);
fileSystemWatcher.Created += new FileSystemEventHandler(fileSystemWatcher_Created);
fileSystemWatcher.Deleted += new FileSystemEventHandler(fileSystemWatcher_Deleted);
fileSystemWatcher.Renamed += new RenamedEventHandler(fileSystemWatcher_Renamed);
fileSystemWatcher.IncludeSubdirectories = false;
fileSystemWatcher.EnableRaisingEvents = true;
Refresh();
}
void fileSystemWatcher_Renamed(object sender, RenamedEventArgs e)
{
RemoveFromBin(e.OldName);
Refresh();
}
void fileSystemWatcher_Deleted(object sender, FileSystemEventArgs e)
{
RemoveFromBin(e.Name);
Refresh();
}
void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
{
Refresh();
}
void fileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
{
Refresh();
}
private void Refresh()
{
// Determine /bin path
string binPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
string newPath = "";
// Copy files to /bin
foreach (string file in Directory.GetFiles(path, extension, SearchOption.TopDirectoryOnly))
{
try
{
DirectoryInfo dInfo = new DirectoryInfo(binPath);
DirectoryInfo[] dirs = dInfo.GetDirectories();
int count = dirs.Count() + 1;
newPath = binPath + "/" + count;
DirectoryInfo dInfo2 = new DirectoryInfo(newPath);
if (!dInfo2.Exists)
dInfo2.Create();
File.Copy(file, System.IO.Path.Combine(newPath, System.IO.Path.GetFileName(file)), true);
}
catch
{
// Not that big deal... Blog readers will probably kill me for this bit of code :-)
}
}
// Create new directory catalog
directoryCatalog = new DirectoryCatalog(newPath, extension);
directoryCatalog.Refresh();
}
public override IQueryable<ComposablePartDefinition> Parts
{
get { return directoryCatalog.Parts; }
}
private void RemoveFromBin(string name)
{
string binPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "");
File.Delete(Path.Combine(binPath, name));
}
So all this actually works, and after the end of the code in main my IEnumerable variable is actually filled with all the plugins in the DLL (which if you follow the code is located in Plugins/1 so that I can modify the dll in the plugins folder).
So now at this point I should be able to re-compile the plugins DLL, drop it in to the Plugins folder, my FileWatcher detect that it's changed, and then copy it into folder "2" and directoryCatalog should point to the new folder. All this
actually works! The problem is, even though it seems like every thing is pointed to the right place, my IEnumerable variable is never updated with the new plugins. So close, but yet so far! Any suggestions?
I know the downsides of doing it this way, that no dll is actually getting unloaded and causing a memory leak, but it's a Windows App and will probably be started at least once a day, and the plugins are un-likely to change
that often, but it's still a requirement from the client that it does this without re-loading the app. Thanks!
Thanks for any help you all can provide, it's driving me crazy not being able to figure this out."