如何将相同程序集的两个版本从两个不同的子文件夹加载到两个不同的域中? [英] How can I load two versions of same assemblies into two different domains from two different subfolders?

查看:119
本文介绍了如何将相同程序集的两个版本从两个不同的子文件夹加载到两个不同的域中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to build a small tool for comparing types in a bunch of assemblies. For this purpose I created two subfolders and put the respective dlls there:


  • .. \Dlls\v1 .1

  • .. \Dlls\v1.2

  • ..\Dlls\v1.1
  • ..\Dlls\v1.2

其中 .. 是应用程序文件夹

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

I also created a proxy object:

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并获取其中声明的所有类型:

and used it for loading in the following routine that should load an dll and get all types declared in it:

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;
}

到目前为止,没有什么不寻常的...但是它并没有像这种情况(可能是由于子文件夹的缘故),所以我进行了一些搜索,发现 app.config 的设置可能会有所帮助,所以我尝试添加两个探测路径:

So far nothing unusual... but it didn't work like that in this case (probably because of the subfolders) so I searched a bit and found that a settting in the app.config might help so I tried to add two probing paths:

<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <probing privatePath="Dlls\v1.1;Dlls\v1.2" />
  </assemblyBinding>
</runtime>

现在没有任何 FileNotFoundExpection 不再,但由于dll具有相同的名称,因此它仅将第一个子目录( v1.1 )中的dll加载到两个域中,因此我将其删除,而是尝试实现 AppDomain.CurrentDomain.AssemblyResolve 事件处理程序是这样的:

Now there wasn't any FileNotFoundExpections anymore but as the dlls have the same names it loaded only dlls from the first subdirectory (v1.1) into both domains so I removed it and instead tried to implement the AppDomain.CurrentDomain.AssemblyResolve event handler like that:

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时都找不到。我没有其他想法可以尝试。

But unfortunatelly with this event handler I created an infinite loop and it calls itself each time it tries to load a dll it cannot find. I have no more ideas what else I could try.

按照@SimonMourier的建议,我尝试对新的AppDomain使用自定义的 .config ,并创建了另外两个 *。config 如:

As @SimonMourier suggested I tried to use a custom .config for my new AppDomains and created two more *.configs like:

<?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>

我将它们命名为 v1.1.config v1.2.config 。然后设置 new ConfigurationFile 属性:

I named them v1.1.config and v1.2.config. Then I set the new ConfigurationFile property:

var appDomainSetup = new AppDomainSetup
{
    ApplicationBase = Environment.CurrentDirectory,
    ConfigurationFile = Path.Combine(Environment.CurrentDirectory, string.Format("{0}.config", version)),        
};

我已设置选项复制到输出目录总是复制;-)

I've set the option Copy to Output Directory to Copy always ;-)

它没有用,所以我谷歌搜索并尝试了另一个建议:

It didn't work so I googled and tried another suggestion:

AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", appDomainSetup.ConfigurationFile);

但这都没有帮助。像我的自定义配置一样,仍然没有 FileNotFoundException

but this didn't help either. Still FileNotFoundException like my custom configs weren't there.

使用 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);

但是,如果我调用 GetData 方法,新的appdomain使用自定义.config:

However if I call the GetData method the new appdomain uses the custom .config:

Debug.WriteLine("Current config: " + AppDomain.CurrentDomain.GetData("APP_CONFIG_FILE"));

它输出我通过 ConfigurationFile设置的路径

这确实令人困惑。堆栈跟踪显示,尽管 GetData 还原了 Assembly.LoadFile 仍然使用原始的.config:

This is really confusing. The Stack Trace reveals that despite of what the GetData returs the Assembly.LoadFile still uses the original .config:


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

=== LOG: This bind starts in default load context. LOG: Using application configuration file:

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

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

LOG:使用主机配置文件:LOG:使用来自以下机器的配置文件

LOG: Using host configuration file: LOG: Using machine configuration file from

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

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






UPDATE-3



好吧,我做了一些进一步的实验,发现通过实现@SimonMourier的建议确实可以工作。 FileNotFoundException 不是由 ProxyDomain LoadFile 方法引发的c>类,但在我应用的 Main 方法中。我猜测程序集和类型只能在 ProxyDomain 上下文中使用,而不能像我尝试的那样转移到主域中。


UPDATE-3

OK, I did some more experimenting and found out that by implementing @SimonMourier suggestion it indeed worked. The FileNotFoundException wasn't thrown by the LoadFile method in the ProxyDomain class but in the Main method of my app. I gues the assembly and the types are allowed to live only within the ProxyDomain context and cannot be transfered into the main domain as I have tried.

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
}

我现在至少需要加载程序集才能重写比较算法;-)

I'll need to rewrite the comparison algorithm now by at least the assemblies can be loaded ;-)

推荐答案

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

If you don't want to recompile the assemblies with a different strong name, than you can load them into different AppDomains, with different setup configuration, and different .config files, exactly like IIS does with multiple web sites, each one in its one AppDomain, completely independent, all hosted in only one AppPool process - w3wp.exe.

这篇关于如何将相同程序集的两个版本从两个不同的子文件夹加载到两个不同的域中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆