【问题标题】:Load assembly with all references that is already loaded使用已加载的所有引用加载程序集
【发布时间】:2017-10-24 17:19:56
【问题描述】:

我想从不是我当前应用程序域基础文件夹的文件夹中加载程序集。我想加载的一些程序集已经在默认应用程序域的旧版本中加载。为了运行它,我找到了以下代码。新加载的程序集位于文件夹 c:\testa\

How to Load an Assembly to AppDomain with all references recursively?

class Program
{
    static void Main(string[] args)
    {
        AppDomainSetup domaininfo = new AppDomainSetup();
        domaininfo.ApplicationBase = @"c:\testa\";
        Evidence adevidence = AppDomain.CurrentDomain.Evidence;
        AppDomain domain = AppDomain.CreateDomain("MyDomain", adevidence, domaininfo);

        Type type = typeof(Proxy);
        var value = (Proxy)domain.CreateInstanceAndUnwrap(
            type.Assembly.FullName,
            type.FullName);

        var assembly = value.GetAssembly(args[0]);
        // AppDomain.Unload(domain);
    }
}

public class Proxy : MarshalByRefObject
{
    public Assembly GetAssembly(string assemblyPath)
    {
        try
        {
            return Assembly.LoadFile(assemblyPath);
        }
        catch (Exception)
        {
            return null;
            // throw new InvalidOperationException(ex);
        }
    }
}

作为上述代码的作者,我得到一个文件或依赖项未找到。那么我该如何处理这个问题。有什么想法吗?

【问题讨论】:

    标签: c# appdomain system.reflection


    【解决方案1】:

    .NET Framework 允许在加载到应用程序域期间通过程序集解析代码进行完全控制。它是通过使用附加到AppDomain.AssemblyResolve 事件的事件处理程序来实现的。事件处理程序的作用是加载程序集并返回程序集实例。只要加载的程序集准备好执行,它的完成方式就取决于实现者。例如,可以动态生成新的程序集,并在加载后返回对它的引用。似乎使用 thet 方法应该可以解决问题,因为此事件将针对主模块、卫星程序集、资源程序集和所有依赖项触发。

    static void Main(string[] args)
    {
        AppDomainSetup domaininfo = new AppDomainSetup();
        domaininfo.ApplicationBase = @"c:\testa\";
        Evidence adevidence = AppDomain.CurrentDomain.Evidence;
        AppDomain domain = AppDomain.CreateDomain("MyDomain", adevidence, domaininfo);
        AssemblyResolver resolver = new AssemblyResolver();
        domain.Assembly += resolver.Resolve;
    
        Type type = typeof(Proxy);
        var value = (Proxy)domain.CreateInstanceAndUnwrap(
            type.Assembly.FullName,
            type.FullName);
    
        var assembly = value.GetAssembly(args[0]);
        // AppDomain.Unload(domain);
    }
    

    使用硬编码的程序集数据实现更复杂的 AssemblyResolver 类的 Resolve 实例方法,在生产中这些数据是从主应用程序程序集配置文件中检索的:

    public Assembly Resolve(object sender, ResolveEventArgs e)
    {
    
        System.Diagnostics.Debug.WriteLine("AssemblyResolve event for: {0}\t\tRequestingAssembly {1}", e.Name, e.RequestingAssembly);
    
        switch(e.Name)
        {
            case "EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=aed0ab2de30e5e00":
                return LoadAssembly(e);
            case "System.Data.SQLite.EF6, Version=1.0.102.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139":
                return LoadAssembly(e);
            case "BlueTechZone.Sqlite.EF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=7741556173e269b9":
                string codeBase = CodeBase + _SubDirectory + "System.Data.SQLite.EF6.dll";
                var ef6SQLite = Assembly.LoadFrom(codeBase);
                return LoadAssembly(e, new String[] { "SQLite.Interop.dll" });
            case "System.Data.SQLite, Version=1.0.102.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139":
                return LoadAssembly(e, new String[] { "SQLite.Interop.dll" });
            default:
                return null;
        }
    }
    

    下面是用于将解析器附加到将在其中运行的同一域的代码,然后将应用程序的其余部分加载到域中。 APP_CONFIG_FILE 用于设置应用程序域使用的配置文件的路径。

    AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", @"E:\src\**\<assembly name>.dll.config");
    
    AssemblyResolver = new AssemblyResolver();
    AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolver.Resolve;
    

    【讨论】:

    • 我测试了解析器。但就我而言,它没有被解雇。
    【解决方案2】:

    我解决了这个问题,因为我改变了程序的流程。

    首先,我将 dll 从当前目录复制到目录 c:\testa。现有文件不会被覆盖。

    然后我启动程序:

    class Program
    {
        static void Main(string[] args)
        {
            /* Copy files from current folder to c:\testa\ */
    
            AppDomainSetup domaininfo = new AppDomainSetup();
            domaininfo.ApplicationBase = @"c:\testa\";
            domaininfo.PrivateBinPath = @"c:\testa\";
            Evidence adevidence = AppDomain.CurrentDomain.Evidence;
            AppDomain domain = AppDomain.CreateDomain("MyDomain", adevidence, domaininfo);
    
            Type type = typeof(Proxy);
            var value = (Proxy)domain.CreateInstanceAndUnwrap(
                type.Assembly.FullName,
                type.FullName);
    
            var assembly = value.Run(() => { /* My Program */});
    
        }
    }
    
    public class Proxy : MarshalByRefObject
    {
        public void GetAssembly(Action a)
        {
            a();
        }
    }
    

    所以我在新的 appdomain 中运行程序,所有程序集都正确加载。

    【讨论】:

      猜你喜欢
      • 2011-10-19
      • 1970-01-01
      • 2021-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-07
      • 1970-01-01
      相关资源
      最近更新 更多