I'm trying to make a simple plugin system in which a plugins are dynamically loaded from .dll files on application's startup and show up in the UI.
This Answer seems to be exactly what I'm looking for. It uses MEF to load the plugins. I tried to create a simple project and follow the instructions. My solution has the following structure:
- MeftTest (contains main MefTest.exe, references only
MefTest.SDK
and not plugins) - MefTest.SDK (contains the
IPlugin.cs
andIPluginViewModel.cs
and the Engine.cs which loads the plugins from the application's directory) - MefTest.Plugin1 (contains first plugin, references
MefTest.SDK
) - MefTest.Plugin2 (contains second plugin, references
MefTest.SDK
)
MefTest.SDK -> IPlugin.cs
public interface IPlugin
{
IPluginViewModel ViewModel { get; }
ResourceDictionary View { get; }
string Title { get; }
}
MefTest.SDK -> Engine.cs
public class Engine
{
[ImportMany]
private IEnumerable<IPlugin> plugins { get; set; }
public async Task<ObservableCollection<IPlugin>> GetPlugins()
{
try
{
var folder = AppDomain.CurrentDomain.BaseDirectory;
var catalog = new AggregateCatalog();
//catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
catalog.Catalogs.Add(new DirectoryCatalog(folder));
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
var result = new ObservableCollection<IPlugin>();
foreach (var p in plugins)
{
result.Add(p);
}
return result;
}
catch (Exception ex)
{
//I get the exception here...
var t = ex;
throw;
}
}
}
MefTest.Plugin1 -> Plugin1.cs
[Export(typeof(IPlugin))]
public class Plugin1 : IPlugin
{
private MainViewModel viewModel { get; set; }
public IPluginViewModel ViewModel
{
get { return viewModel; }
}
public ResourceDictionary View
{
get { return viewDictionary; }
}
private ResourceDictionary viewDictionary = new ResourceDictionary();
public string Title
{
get { return "Plugin 1"; }
}
[ImportingConstructor]
public Plugin1()
{
//I get the error here. tried both of these, none of them work
viewDictionary.Source =
// new Uri("pack://application:,,,/MefTest.Plugin1;component/views/main.xaml", UriKind.Absolute);
new Uri("/MefTest.Plugin1;component/views/main.xaml",
UriKind.Relative);
}
public override string ToString()
{
return Title;
}
}
however, I get the error Could not load file or assembly 'MefTest.Plugin1.dll, Culture=neutral' or one of its dependencies. The system cannot find the file specified.
The Main.xaml
file is in MefTest.Plugin1\Views\Main.xaml
folder. Output type of the project is ClassLibrary
and Build Action of the xaml file is Page
.
PS: I tried to reference the plugin directly and add it without the MEF (Plugins.Add(new Plugin3.Plugin3());
) and it still threw the same exception. So I don't think the problem is with the MEF part of the solution.
How can I fix this? Also, is there a better option to this approach?