我有一个主应用程序,它将插件加载到单独的应用程序域中。在该插件中,我订阅了从主应用程序传递的对象上的事件。在单独的应用程序域中创建插件会将插件 dll 加载到该域中,但订阅主应用程序对象上的事件将导致插件 dll 也被加载到主应用程序域中。
我希望能够卸载插件域(有效),但我还需要能够替换我目前无法执行的插件 dll,因为该 dll 已加载到主应用程序域中(因为该订阅)。
这是一个类似于我的实际代码的示例代码(为简洁起见):
我有一个名为 IContract 的接口,用于实例化插件:
namespace PluginShared
{
public interface IContract
{
void Init(IHostObject hostObject);
void Shutdown();
}
}
它驻留在一个单独的 PluginShared.dll 中。该接口定义了 2 个方法,Init 用于传递在主应用程序中创建的对象,而 Shutdown 用于在插件的应用程序域卸载之前在插件中进行必要的清理。
IHostObject 位于同一个 PluginShared.dll 中,如下所示:
namespace PluginShared
{
public interface IHostObject
{
event Action ValueChanged;
}
}
该接口由主应用程序中的 HostObject 实现:
public class HostObject : MarshalByRefObject, IHostObject
{
public event Action ValueChanged;
public void Foo()
{
if (ValueChanged != null)
ValueChanged();
}
}
主应用程序代码加载插件,调用 Init 并将 HostObject 实例传递给插件。然后在插件上调用 Shutdown 并卸载插件域。
private void LoadUnloadPlugin()
{
IHostObject hostObject = new HostObject();
AppDomain pluginAppDomain = AppDomain.CreateDomain("PluginAppDomain");
IContract plugin = (IContract)pluginAppDomain.CreateInstanceFromAndUnwrap(@".\Plugin1.dll", "Plugin1.Plugin1");
plugin.Init(hostObject);
plugin.Shutdown();
AppDomain.Unload(pluginAppDomain);
}
该插件是它自己的 Plugin1.dll:
namespace Plugin1
{
public class Plugin1 : MarshalByRefObject, IContract
{
IHostObject myHostObject;
public void Init(IHostObject hostObject)
{
myHostObject = hostObject;
myHostObject.ValueChanged += OnValueChanged;
}
public void Shutdown()
{
myHostObject.ValueChanged -= OnValueChanged;
myHostObject = null;
}
private void OnValueChanged()
{
// Do Stuff
}
}
}
同样,问题是当 myHostObject.ValueChanged += OnValueChanged; 执行时,它会将 Plugin1.dll 加载到主应用程序域中,这将锁定 dll,从而阻止我删除它。
顺便提一句。我有许多类似 IHostObject 的接口,它们定义了由不同对象触发的不同 ValueChanged 类事件。同样一般来说,我的插件不会订阅所有对象的事件。我之所以提到这一点,是因为上面示例代码中的 ValueChanged 事件可以很容易地替换为 IContract 中的方法,该方法将为每个插件调用而不是触发事件,但我的实际实现需要更多类似事件的方法。
那么有没有一种方法/技术/模式我可以使用以便能够从主应用程序中获取选定的通知/事件,而无需将插件 dll 加载到主应用程序域中?