我有一个主应用程序,它将插件加载到一个单独的应用程序域中。在该插件中,我订阅了从主应用程序传递的对象上的事件。在单独的应用程序域中创建插件会将插件dll加载到该域中,但订阅主应用程序对象上的事件也会导致插件dll也被加载到主应用程序域中。
我希望能够卸载插件域(有效)但我还需要能够替换我目前无法执行的插件dll,因为该dll是在主应用域中加载的(因为该订阅)
以下示例代码类似于我的实际代码(为简洁起见):
我有一个名为IContract的接口,用于实例化插件:
namespace PluginShared
{
public interface IContract
{
void Init(IHostObject hostObject);
void Shutdown();
}
}
它驻留在一个单独的PluginShared.dll中。该接口定义了2个方法,Init用于传递在主应用程序中创建的对象,Shutdown用于在插件的app域被卸载之前在插件中进行必要的清理。
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加载到主app域中,这将锁定dll,从而阻止我删除它。
顺便说一下。我有许多类似IHostObject的接口,它们定义不同的ValueChanged,就像不同对象触发的事件一样。通常,我的插件不会订阅所有对象的事件。我之所以提到这一点,是因为上面示例代码中的ValueChanged事件可以很容易地用IContract中的一个方法替换,该方法将为每个插件调用而不是触发事件,但我的实际实现需要更多类似事件的方法。
那么有没有一种方法/技术/模式可以用来从主应用程序中获取选定的通知/事件而无需将插件dll加载到主应用程序域?