这个话题大致涵盖了我的问题。我看过关于这个问题的最佳实践文档,但它并没有真正描述如何摆脱这种情况。http://msdn.microsoft.com/en-us/library/dd153782.aspx
我的情况。路径一:主要的应用程序。(有签名,版本号。.net 3.5)通用接口程序集x (Signed, versioned)
路径B:用户大会。通用接口程序集x(有签名,有版本)(可能与上面的版本不同)。可能不知道实际的版本,因为我们无法控制客户/用户做什么。
我们的主应用程序使用反射从用户程序集加载指定的类,并使用公共接口程序集X中定义的指定接口与类通信。应用程序必须保持在一个特定的路径中,而用户(业务)程序集必须保持在另一个路径中。别叫我改,我管不了。
问题是,在路径B中很可能会有一个公共接口程序集X的副本。因此,当我使用反射加载用户程序集时,它会自动从相同路径加载公共接口程序集X,而不是使用已经在应用程序内存中加载的程序集X。最后,iXXX类型与iXXX类型不相同,或者找不到用户程序集类的构造函数,因为它再次混淆了公共接口程序集x的两个副本之间的接口。
那么,我怎么能去加载,并创建一个类从用户程序集在路径B,但不让它从路径B加载公共接口程序集X(并使用一个从路径a,已经在内存中)?
更多信息:我得到的大多数问题都发生在CreateInstance周围,因为ClientContext是在公共接口汇编X中,所以当我从路径A中创建一个时,我的对话框中的构造函数似乎期待路径b中的构造函数。然后我以"类型上的构造函数"ExternalTestForms结束。WindowDemo1' not found."错误。(即使在路径A和路径B中使用完全相同的[signed and versioned]程序集,这个错误仍然发生。)
我认为这涵盖了我用来加载程序集和使用接口创建自定义窗口的代码。
public IExternalForm ShowExternalWindow(string filename, string classname, string parameters, string formIdentity)
{
if (File.Exists(filename))
{
ClientContext context = GetCurrentClientContext(parameters, formIdentity);
object dialog = null;
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += this.CurrentDomain_AssemblyResolve;
this.basePath = Path.GetDirectoryName(filename);
Assembly externalAssembly = Assembly.LoadFile(filename);
try
{
// The Window must must have a ClientContext defined in the constructor.
dialog = externalAssembly.CreateInstance(classname, false, BindingFlags.CreateInstance, null, new object[] { context }, context.Desktop.Culture, null);
}
catch (Exception ex)
{
// MessageBox "Could not launch the Window"
// LogTraceException
}
if (dialog != null && dialog is IExternalForm && dialog is System.Windows.Window)
{
System.Windows.Window window = (System.Windows.Window)dialog;
...
...
...
}
...
}
}
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string filename = args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll";
// Seek out the specified assembly in the current Application path first.
string assemblyPath = Path.Combine(Application.StartupPath, filename);
if (!File.Exists(assemblyPath))
{
assemblyPath = Path.Combine(this.basePath, filename);
}
if (File.Exists(assemblyPath))
{
// Load the assembly from the specified path.
Assembly myAssembly = Assembly.LoadFrom(assemblyPath);
// Return the loaded assembly.
return myAssembly;
}
return null;
}
考虑几个不同的低影响选项:
1)在应用程序启动时,只将客户程序集复制到主文件夹中(我们要求客户程序集具有特定的命名约定以便加载,因此这对我们来说很容易实现)。
2)在应用程序启动时,删除客户目录中所有标准dll的副本,以防止冲突。