在我的项目中,我需要使用插件。但是要在我的项目中使用这些,我需要导入插件的引用。由于我不知道项目预先使用了多少或哪些插件,我想在我的项目中动态导入它们。
String path = Application.StartupPath;
string[] pluginFiles = Directory.GetFiles(path, "*.dll");
ipi = new IPlugin[pluginFiles.Length];
Assembly asm;
for (int i = 0; i < pluginFiles.Length; i++)
{
string args = pluginFiles[i].Substring(
pluginFiles[i].LastIndexOf("\\") + 1,
pluginFiles[i].IndexOf(".dll") -
pluginFiles[i].LastIndexOf("\\") - 1);
asm = Assembly.LoadFile(pluginFiles[i]);
Type[] types = asm.GetTypes();
在这个代码示例中,我搜索了所有.dll文件并将它们放入字符串列表中。 但是我现在如何加载所有这些dll文件?或者有没有办法在不真正导入它们的情况下使用这些dll文件?
答案 0 :(得分:16)
您需要将 System.ComponentModel.Composition 的引用添加到使用MEF的导入/导出功能的项目中。
首先,bootstrapper / loader(在我的例子中,我刚刚将它添加到Main类中)。
<强> Program.cs的强>:
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using MEFContract;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
var prgm = new Program();
// Search the "Plugins" subdirectory for assemblies that match the imports.
var catalog = new DirectoryCatalog("Plugins");
using (var container = new CompositionContainer(catalog))
{
// Match Imports in "prgm" object with corresponding exports in all catalogs in the container
container.ComposeParts(prgm);
}
prgm.DoStuff();
Console.Read();
}
private void DoStuff()
{
foreach (var plugin in Plugins)
plugin.DoPluginStuff();
}
[ImportMany] // This is a signal to the MEF framework to load all matching exported assemblies.
private IEnumerable<IPlugin> Plugins { get; set; }
}
}
IPlugin接口是导入和输入之间的契约。出口。所有插件都将实现此接口。合同很简单:
<强> IPlugin.cs 强>:
namespace MEFContract
{
public interface IPlugin
{
void DoPluginStuff();
}
}
最后,您可以在不同的程序集中创建任意数量的插件。它们必须实现合同接口,并且还要使用“导出”属性进行修饰,以向MEF指示它们应与任何相应的导入匹配。然后将dll放在“Plugins”文件夹中(此文件夹应与可执行文件位于同一位置)。这是一个示例插件:
<强> Plugin.cs 强>:
using System;
using System.ComponentModel.Composition;
using MEFContract;
namespace Plugin
{
[Export(typeof(IPlugin))]
public class Plugin : IPlugin
{
public void DoPluginStuff()
{
Console.WriteLine("Doing my thing!");
}
}
}
答案 1 :(得分:4)
为简单起见,我们假设IPlugin
的所有实现都有默认构造函数(public和no parameters)。
那就是说,你真的想找到实现这个接口的所有类型并创建它们的实例。你在某种程度上走在了正确的轨道上,但你可以通过一点LINQ来简化这一过程:
String path = Application.StartupPath;
string[] pluginFiles = Directory.GetFiles(path, "*.dll");
ipi = (
// From each file in the files.
from file in pluginFiles
// Load the assembly.
let asm = Assembly.LoadFile(file)
// For every type in the assembly that is visible outside of
// the assembly.
from type in asm.GetExportedTypes()
// Where the type implements the interface.
where typeof(IPlugin).IsAssignableFrom(type)
// Create the instance.
select (IPlugin) Activator.CreateInstance(type)
// Materialize to an array.
).ToArray();
也就是说,使用依赖注入框架可能会更好;它们通常允许动态加载和绑定到编译时未引用的程序集中的接口实现。
此外,虽然有点令人费解(在我看来),但您可能需要查看System.AddIn
namespaces,因为它们是专门为此目的而构建的。但是,如果您不必担心合同的版本控制等,依赖注入路径通常会更容易
答案 2 :(得分:1)
我有一个应用程序,它不仅可以在运行时加载插件,还可以在用户将它们放入文件夹时将其加载和卸载,将它们取出或删除它们。所以,当我需要重新编译我的插件时,我不需要重新启动我的应用程序。在我的例子中,所有插件都来自插件摘要,因此很容易在.DLL中找到它们。
这是我的加载方法:
private static void LoadPlugins(FileInfo file)
{
try
{
Assembly assembly = Assembly.LoadFrom(file.FullName);
foreach (Type type in assembly.GetTypes())
{
if (type.IsSubclassOf(typeof(Plugin)) && type.IsAbstract == false)
{
Plugin b = type.InvokeMember(null,
BindingFlags.CreateInstance,
null, null, null) as Plugin;
plugins.Add(new PluginWrapper(b, file));
b.Register();
}
}
}
catch (ReflectionTypeLoadException ex)
{
StringBuilder sb = new StringBuilder();
foreach (Exception exSub in ex.LoaderExceptions)
{
sb.AppendLine(exSub.Message);
if (exSub is FileNotFoundException)
{
FileNotFoundException exFileNotFound = exSub as FileNotFoundException;
if (!string.IsNullOrEmpty(exFileNotFound.FusionLog))
{
sb.AppendLine("Fusion Log:");
sb.AppendLine(exFileNotFound.FusionLog);
}
}
sb.AppendLine();
}
string errorMessage = sb.ToString();
Log.Error("Plugins Manager", errorMessage);
}
}