简体   繁体   English

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

[英]How can I load two versions of same assemblies into two different domains from two different subfolders?

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: 为此我创建了两个子文件夹并将各自的dll放在那里:

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

where .. is the application folder 其中..是应用程序文件夹

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

and used it for loading in the following routine that should load an dll and get all types declared in it: 并使用它来加载以下例程,该例程应加载一个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;
}

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: 到目前为止没有什么不寻常的......但是在这种情况下它没有像那样工作(可能是因为子文件夹)所以我搜索了一下,发现app.config中的设置可能有帮助所以我试图添加两个探测路径:

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

Now there wasn't any FileNotFoundExpection s 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: 现在没有任何FileNotFoundExpection了,但由于dll具有相同的名称,它只将dll从第一个子目录( v1.1 )加载到两个域中,因此我将其删除,而是尝试实现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;
};

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. 但不幸的是,我使用这个事件处理程序创建了一个无限循环,它每次尝试加载它找不到的dll时都会调用它自己 I have no more ideas what else I could try. 我没有更多的想法我还能尝试什么。


UPDATE-1 UPDATE-1

As @SimonMourier suggested I tried to use a custom .config for my new AppDomains and created two more *.config s like: 正如@SimonMourier建议我尝试为我的新AppDomains使用自定义.config并创建另外两个*.config如:

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

I named them v1.1.config and v1.2.config . 我将它们命名为v1.1.configv1.2.config Then I set the new ConfigurationFile property: 然后我设置新的 ConfigurationFile属性:

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 ;-) 我已经设置Copy to Output Directory的选项Copy always ;-)

It didn't work so I googled and tried another suggestion: 它没有用,所以我用Google搜索并尝试了另一个建议:

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

but this didn't help either. 但这也没有帮助。 Still FileNotFoundException like my custom configs weren't there. 仍然像我的自定义配置FileNotFoundException不存在。

Using the SetConfigurationBytes method instead had also no effect: 使用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);

However if I call the GetData method the new appdomain uses the custom .config: 但是,如果我调用GetData方法,新的appdomain将使用自定义.config:

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

It outputs the path that I set via ConfigurationFile 它输出我通过ConfigurationFile设置的路径


UPDATE-2 UPDATE-2

This is really confusing. 这真令人困惑。 The Stack Trace reveals that despite of what the GetData returs the Assembly.LoadFile still uses the original .config: Stack Trace显示,尽管GetData重新调整了Assembly.LoadFile仍使用原始的.config:

=== LOG: This bind starts in default load context. ===日志:此绑定在默认加载上下文中启动。 LOG: Using application configuration file: 日志:使用应用程序配置文件:

C:[...]\\bin\\Debug\\MyApp.exe.Config C:[...] \\斌\\调试\\ MyApp.exe.Config

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 \\框架\\ v4.0.30319 \\ CONFIG \\ machine.config中。


UPDATE-3 UPDATE-3

OK, I did some more experimenting and found out that by implementing @SimonMourier suggestion it indeed worked. 好的,我做了一些实验,发现通过实施@SimonMourier建议确实有效。 The FileNotFoundException wasn't thrown by the LoadFile method in the ProxyDomain class but in the Main method of my app. ProxyDomain类中的LoadFile方法没有抛出FileNotFoundException ,而是在我的应用程序的Main方法中抛出。 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. 我猜测程序集和类型只允许在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;
    }
}

Method in the main domain: 主域中的方法:

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对多个Web站点一样,每个站点都在其中一个AppDomain,完全独立,全部只托管在一个AppPool进程中 - w3wp.exe。

Strong name your dll . 强烈的名字你的DLL。 Make 2 exe to run separate app domain. 使2 exe运行单独的应用程序域。

Otherwise there is no way to reference same dll name twice. 否则,无法两次引用相同的dll名称。 You have to rename new dll with _new.dll and then you can use it with fully qualified names pace class method name format. 您必须使用_new.dll重命名新的dll,然后您可以使用完全限定名称的起搏级方法名称格式。

Meaning rename all v2 dll 意思是重命名所有v2 dll

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何从2个不同的程序集加载相同的插件,并在两者之间序列化和反序列化类型? C#.net - How to load same plugins from 2 different assemblies, and serializing and deserializing types between the two? C# .net 在两个不同的appdomain中运行两个相同的程序集 - Running two same assemblies in two different appdomains 相同的DLL两个不同版本 - Same DLLs two different versions 一个进程是否可以加载两个同名但版本不同的dll? - Is it possible for a process to load two dll with same name but different versions? 在两个不同的程序集中,两个具有相同名称的类可以位于相同的名称空间中吗? - Can two classes with the same name be in the same namespace across two different assemblies? 可以在一个项目中引用两个具有相同名称的不同程序集吗? 不同的二进制文件 - Possible to reference two different assemblies with the same name in a project? different binaries 如何在两个不同域中的两个不同上下文中使用一个表? - How to use one table for two different contexts in two different domains? 我怎么知道来自两个不同邮箱的两个电子邮件是相同的? - How can I know that two emails are the same from two different mailboxes? 流畅的 NHibernate C#。 我如何从两个不同的程序集中使用表模型(实体)和她的 Map 类? - Fluent NHibernate C#. How I can use table model(entity)and her Map class from two different assemblies? 如何处理对具有相同导出的程序集的两个引用? - How can I handle two references to assemblies that have the same exports?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM