代码之家  ›  专栏  ›  技术社区  ›  Daniel Schaffer

是否有方法强制将所有引用的程序集加载到应用程序域中?

  •  66
  • Daniel Schaffer  · 技术社区  · 15 年前

    我的项目设置如下:

    • 项目“定义”
    • “实施”项目
    • “消费者”项目

    项目“使用者”同时引用“定义”和“实现”,但不静态引用“实现”中的任何类型。

    当应用程序启动时,项目“consumer”调用“definition”中的静态方法,该方法需要在“implementation”中查找类型。

    是否有一种方法可以强制将任何引用的程序集加载到应用程序域中,而不必知道路径或名称,最好不必使用成熟的IOC框架?

    6 回复  |  直到 7 年前
        1
  •  77
  •   Daniel Schaffer    14 年前

    这似乎起到了关键作用:

            var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
            var loadedPaths = loadedAssemblies.Select(a => a.Location).ToArray();
    
            var referencedPaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll");
            var toLoad = referencedPaths.Where(r => !loadedPaths.Contains(r, StringComparer.InvariantCultureIgnoreCase)).ToList();
            toLoad.ForEach(path => loadedAssemblies.Add(AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(path))));
    

    正如jon所指出的,理想的解决方案需要递归到每个已加载的程序集的依赖项中,但是在我的特定场景中,我不必担心它。


    更新: .NET4中包含的托管扩展性框架(system.componentmodel)具有更好的工具来完成类似的事情。

        2
  •  52
  •   Jon Skeet    15 年前

    你可以用 Assembly.GetReferencedAssemblies 得到一个 AssemblyName[] ,然后打电话 Assembly.Load(AssemblyName) 在他们每个人身上。当然,您需要重复执行-但最好跟踪已加载的程序集:)

        3
  •  16
  •   jmelhus    10 年前

    只想共享一个递归示例。我在启动例程中调用loadReferencedAssembly方法,如下所示:

    foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
    {
        this.LoadReferencedAssembly(assembly);
    }
    

    这是递归方法:

    private void LoadReferencedAssembly(Assembly assembly)
    {
        foreach (AssemblyName name in assembly.GetReferencedAssemblies())
        {
            if (!AppDomain.CurrentDomain.GetAssemblies().Any(a => a.FullName == name.FullName))
            {
                this.LoadReferencedAssembly(Assembly.Load(name));
            }
        }
    }
    
        4
  •  12
  •   Meirion Hughes    11 年前

    如果使用fody.costura或任何其他程序集合并解决方案,接受的答案将不起作用。

    下面加载当前加载的任何程序集的引用程序集。递归留给您。

    var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
    
    loadedAssemblies
        .SelectMany(x => x.GetReferencedAssemblies())
        .Distinct()
        .Where(y => loadedAssemblies.Any((a) => a.FullName == y.FullName) == false)
        .ToList()
        .ForEach(x => loadedAssemblies.Add(AppDomain.CurrentDomain.Load(x)));
    
        5
  •  0
  •   John M. Wright JMF    7 年前

    由于今天必须从特定路径加载一个程序集+依赖项,所以我编写了这个类来完成它。

    public static class AssemblyLoader
    {
        private static readonly ConcurrentDictionary<string, bool> AssemblyDirectories = new ConcurrentDictionary<string, bool>();
    
        static AssemblyLoader()
        {
            AssemblyDirectories[GetExecutingAssemblyDirectory()] = true;
            AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
    
        }
    
        public static Assembly LoadWithDependencies(string assemblyPath)
        {
            AssemblyDirectories[Path.GetDirectoryName(assemblyPath)] = true;
            return Assembly.LoadFile(assemblyPath);
        }
    
        private static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
        {
            string dependentAssemblyName = args.Name.Split(',')[0] + ".dll";
            List<string> directoriesToScan = AssemblyDirectories.Keys.ToList();
    
            foreach (string directoryToScan in directoriesToScan)
            {
                string dependentAssemblyPath = Path.Combine(directoryToScan, dependentAssemblyName);
                if (File.Exists(dependentAssemblyPath))
                    return LoadWithDependencies(dependentAssemblyPath);
            }
            return null;
        }
    
        private static string GetExecutingAssemblyDirectory()
        {
            string codeBase = Assembly.GetExecutingAssembly().CodeBase;
            var uri = new UriBuilder(codeBase);
            string path = Uri.UnescapeDataString(uri.Path);
            return Path.GetDirectoryName(path);
        }
    }
    
        6
  •  0
  •   Arsen Khachaturyan    7 年前

    另一个版本(基于 Daniel Schaffer 答案)在这种情况下,您可能不需要加载所有程序集,但需要预定义的程序集数量:

    var assembliesToLoad = { "MY_SLN.PROJECT_1", "MY_SLN.PROJECT_2" };
    
    // First trying to get all in above list, however this might not 
    // load all of them, because CLR will exclude the ones 
    // which are not used in the code
    List<Assembly> dataAssembliesNames =
       AppDomain.CurrentDomain.GetAssemblies()
                .Where(assembly => AssembliesToLoad.Any(a => assembly.GetName().Name == a))
                .ToList();
    
    var loadedPaths = dataAssembliesNames.Select(a => a.Location).ToArray();
    
    var compareConfig = StringComparison.InvariantCultureIgnoreCase;
    var referencedPaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll")
        .Where(f =>
        {
           // filtering the ones which are in above list
           var lastIndexOf = f.LastIndexOf("\\", compareConfig);
           var dllIndex = f.LastIndexOf(".dll", compareConfig);
    
           if (-1 == lastIndexOf || -1 == dllIndex)
           {
              return false;
           }
    
           return AssembliesToLoad.Any(aName => aName == 
              f.Substring(lastIndexOf + 1, dllIndex - lastIndexOf - 1));
         });
    
    var toLoad = referencedPaths.Where(r => !loadedPaths.Contains(r, StringComparer.InvariantCultureIgnoreCase)).ToList();
    
    toLoad.ForEach(path => dataAssembliesNames.Add(AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(path))));
    
    if (dataAssembliesNames.Count() != AssembliesToLoad.Length)
    {
       throw new Exception("Not all assemblies were loaded into the  project!");
    }