C# 如何将相同程序集的两个版本从两个不同的子文件夹加载到两个不同的域中?

C# 如何将相同程序集的两个版本从两个不同的子文件夹加载到两个不同的域中?,c#,.net,dll,.net-4.5,appdomain,C#,.net,Dll,.net 4.5,Appdomain,我正在尝试构建一个小工具,用于比较一组程序集中的类型。为此,我创建了两个子文件夹,并将相应的DLL放在其中: 。\Dlls\v1.1 。\Dlls\v1.2 其中。是应用程序文件夹 我还创建了一个代理对象: public class ProxyDomain : MarshalByRefObject { public Assembly LoadFile(string assemblyPath) { try { //Debu

我正在尝试构建一个小工具,用于比较一组程序集中的类型。为此,我创建了两个子文件夹,并将相应的DLL放在其中:

  • 。\Dlls\v1.1
  • 。\Dlls\v1.2
其中
是应用程序文件夹

我还创建了一个代理对象:

public class ProxyDomain : MarshalByRefObject
{
    public Assembly LoadFile(string assemblyPath)
    {
        try
        {
            //Debug.WriteLine("CurrentDomain = " + AppDomain.CurrentDomain.FriendlyName);
            return Assembly.LoadFile(assemblyPath);
        }
        catch (FileNotFoundException)
        {
            return null;
        }
    }
}
并将其用于加载以下例程,该例程应加载dll并在其中声明所有类型:

private static HashSet<Type> LoadAssemblies(string version)
{
    _currentVersion = version;

    var path = Path.Combine(Environment.CurrentDirectory, Path.Combine(_assemblyDirectory, version));

    var appDomainSetup = new AppDomainSetup
    {
        ApplicationBase = Environment.CurrentDirectory,     
    };

    var evidence = AppDomain.CurrentDomain.Evidence;
    var appDomain = AppDomain.CreateDomain("AppDomain" + version, evidence, appDomainSetup);           

    var proxyDomainType = typeof(ProxyDomain);
    var proxyDomain = (ProxyDomain)appDomain.CreateInstanceAndUnwrap(proxyDomainType.Assembly.FullName, proxyDomainType.FullName);
    _currentProxyDomain = proxyDomain;
    
    var assemblies = new HashSet<Type>();
    
    var files = Directory.GetFiles(path, "*.dll");

    foreach (var file in files)
    {
        try
        {
            var assembly = proxyDomain.LoadFile(file);
            if (assembly != null)
            {
                assemblies.UnionWith(assembly.DefinedTypes.Where(t => t.IsPublic));
            }
        }
        catch (Exception)
        {
        }
    }
    return assemblies;
}
现在不再有任何
FileNotFoundExpection
s,但由于DLL具有相同的名称,它只将第一个子目录(
v1.1
)中的DLL加载到两个域中,因此我将其删除,并尝试实现
AppDomain.CurrentDomain.AssemblyResolve
事件处理程序,如下所示:

AppDomain.CurrentDomain.AssemblyResolve += (sender, e) =>
{
    var path = Path.Combine(Environment.CurrentDirectory, Path.Combine(_assemblyDirectory, _currentVersion));
    path = Path.Combine(path, e.Name.Split(',').First());
    path = path + ".dll";
    var assembly = _currentProxyDomain.LoadFile(path);
    return assembly;
};
但不幸的是,使用这个事件处理程序,我创建了一个无限循环,每次尝试加载找不到的dll时,它都会调用自己。我不知道我还能尝试什么


更新-1 正如@SimonMourier所建议的,我尝试为我的新AppDomains使用一个自定义的
.config
,并创建了另外两个
*.config
s,如:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <probing privatePath="Dlls\v1.1" />
    </assemblyBinding>
  </runtime>
</configuration>
我已将选项
复制到输出目录
设置为
始终复制
;-)

它不起作用,所以我在谷歌上搜索并尝试了另一个建议:

AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", appDomainSetup.ConfigurationFile);
但这也没用。仍然
FileNotFoundException
就像我的自定义配置不存在一样

相反,使用
SetConfigurationBytes
方法也没有效果:

var domainConfig = @"
    <configuration>
        <startup>
            <supportedRuntime version=""v4.0"" sku="".NETFramework,Version=v4.5"" />
        </startup>
        <runtime>
            <assemblyBinding xmlns=""urn:schemas-microsoft-com:asm.v1"">
              <probing privatePath=""Dlls\{0}"" />
            </assemblyBinding>
        </runtime>
    </configuration>";

domainConfig = string.Format(domainConfig, version).Trim();
var probingBytes = Encoding.UTF8.GetBytes(domainConfig);
appDomainSetup.SetConfigurationBytes(probingBytes);
它输出我通过
ConfigurationFile


更新-2 这真令人困惑。堆栈跟踪显示,尽管
GetData
重新调用了
Assembly.LoadFile
,但仍然使用原始的.config:

==日志:此绑定在默认加载上下文中启动。日志:使用应用程序配置文件:

C:[…]\bin\Debug\MyApp.exe.Config

日志:使用主机配置文件:日志:使用来自的计算机配置文件

C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config


更新-3 好的,我做了更多的实验,发现通过实施@SimonMourier建议,它确实有效。
FileNotFoundException
不是由
ProxyDomain
类中的
LoadFile
方法引发的,而是在我的应用程序的
Main
方法中引发的。我认为程序集和类型只允许存在于
ProxyDomain
上下文中,并且不能像我尝试的那样转移到主域中

public IEnumerable<Type> LoadFile(string assemblyPath)
{
    //try
    {
        // does't throw any exceptions
        var assembly = Assembly.LoadFile(assemblyPath);
        // returning the assembly itself or its types will throw an exception in the main application
        return assembly.DefinedTypes;
    }
    //catch (FileNotFoundException)
    {
      //  return null;
    }
}
public IEnumerable加载文件(字符串汇编路径)
{
//试一试
{
//不抛出任何异常
var assembly=assembly.LoadFile(assemblyPath);
//返回程序集本身或其类型将在主应用程序中引发异常
返回assembly.DefinedTypes;
}
//捕获(FileNotFoundException)
{
//返回null;
}
}
主域中的方法:

private static HashSet<Type> LoadAssemblies(string version)
{
    // omitted

    foreach (var file in files)
    {
        //try
        {

            // the exception occurs here, when transfering types between domains:
            var types = proxyDomain.LoadFile(file);         
            assemblies.UnionWith(types.Where(t => t.IsPublic));
        }
    }

    // omitted
}
私有静态HashSet加载程序集(字符串版本)
{
//省略
foreach(文件中的var文件)
{
//试一试
{
//在域之间传输类型时,此处出现异常:
var types=proxyDomain.LoadFile(文件);
UnionWith(类型,其中(t=>t.IsPublic));
}
}
//省略
}
我现在需要重写比较算法,至少可以加载程序集;-)

Strong为dll命名。 使2个exe运行单独的应用程序域

否则,无法引用同一dll名称两次。 必须使用_new.dll重命名新dll,然后才能将其与完全限定名pace类方法名格式一起使用


这意味着重命名所有v2 dll

如果您不想用不同的强名称重新编译程序集,则可以将它们加载到不同的AppDomain中,使用不同的安装配置和不同的.config文件,就像IIS对多个网站所做的那样,每个网站都在其一个AppDomain中,完全独立,所有这些都只托管在一个AppPool进程中—w3wp.exe。

这些是第三方DLL,我必须重新编译它们以创建强名称。我宁愿避免这样。我也不想引用那些dll,只是通过编程方式将它们加载到我为此目的创建的两个附加域中,因为正如您所说,不可能将多个同名dll加载到同一个域中。我还认为重命名也没有什么帮助,因为DLL(大约80个)相互引用,所以我不得不强制加载它们。如果你不想,将它们加载到两个不同的域(使用不同的.config)似乎是个不错的主意recompile@SimonMourier我尝试了你的建议,可惜没有成功:-(请参阅我的问题更新-1。@SimonMourier如果您将您的评论作为答案发布,我可以接受;-)您也可能会发现第三次更新很有趣,它解释了为什么我起初认为我不会工作。
Debug.WriteLine("Current config: " + AppDomain.CurrentDomain.GetData("APP_CONFIG_FILE"));
public IEnumerable<Type> LoadFile(string assemblyPath)
{
    //try
    {
        // does't throw any exceptions
        var assembly = Assembly.LoadFile(assemblyPath);
        // returning the assembly itself or its types will throw an exception in the main application
        return assembly.DefinedTypes;
    }
    //catch (FileNotFoundException)
    {
      //  return null;
    }
}
private static HashSet<Type> LoadAssemblies(string version)
{
    // omitted

    foreach (var file in files)
    {
        //try
        {

            // the exception occurs here, when transfering types between domains:
            var types = proxyDomain.LoadFile(file);         
            assemblies.UnionWith(types.Where(t => t.IsPublic));
        }
    }

    // omitted
}