有没有办法强制所有引用的程序集加载到应用程序域?

时间:2010-03-05 04:40:16

标签: c# assemblies appdomain

我的项目设置如下:

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

项目“消费者”引用“定义”和“实施”,但不静态引用“实施”中的任何类型。

当应用程序启动时,Project“Consumer”会在“Definition”中调用静态方法,该方法需要在“Implementation”中找到类型

有没有办法可以在不知道路径或名称的情况下强制将任何引用的程序集加载到App域中,最好不必使用完整的IOC框架?

10 个答案:

答案 0 :(得分:78)

这似乎可以解决问题:

        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所说,理想的解决方案需要递归到每个已加载程序集的依赖项中,但在我的特定场景中,我不必担心它。


更新:.NET 4中包含的托管扩展性框架(System.ComponentModel)有更好的工具来完成这样的事情。

答案 1 :(得分:53)

您可以使用Assembly.GetReferencedAssemblies获取AssemblyName[],然后在每个Assembly.Load(AssemblyName)上调用{{1}}。当然,你需要递归 - 但最好是跟踪你已装载的装配体。)

答案 2 :(得分:17)

只是想分享一个递归的例子。我在我的启动例程中调用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));
        }
    }
}

答案 3 :(得分:13)

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

以下加载任何当前加载的Assembly的Referenced Assemblies。递归留给你。

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

答案 4 :(得分:0)

因为我今天必须从特定路径加载程序集+依赖项,所以我写了这个类来做它。

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

答案 5 :(得分:0)

当您可能不需要加载所有程序集但预定义数量的程序集时,会出现另一个版本(基于Daniel Schaffer答案):

Click

答案 6 :(得分:0)

如果您的程序集在编译时未引用任何代码,则即使您已添加项目或nuget程序包作为参考,这些程序集也不会作为对其他程序集的引用。这与DebugRelease的构建设置,代码优化等无关。在这些情况下,必须显式调用Assembly.LoadFrom(dllFileName)才能加载程序集。

答案 7 :(得分:0)

要通过名称获取引用的程序集,可以使用以下方法:

class AtypicalSurfaceWork extends Surface {
  #id;
  #name;
  #x;
  #y;
  #value;
  #periphery;
  #eventSender;
  // Declarations had to be included
  constructor({ id, name, x, y, eventSender }) {
    super({ id, name, x, y });
    this.#eventSender = eventSender;
  }
  set x(value) {
    // Super doesn't work, so I rewrote this part of code
    console.log("Test x");
    this.#x = parseFloat(value || 0);
    this.calculate();
    this.#eventSender.emit("calculate");
  }
  set y(value) {
    // It prints out, so it executes
    console.log("Test y");
    this.#y = parseFloat(value || 0);
    this.calculate();
    this.#eventSender.emit("calculate");
  }
  get surface() {
    return this.value;
  }
}

答案 8 :(得分:0)

在我的winforms应用程序中,我使JavaScript(在WebView2控件中)可以调用各种.NET内容,例如,程序集Microsoft.VisualBasic.dll中的Microsoft.VisualBasic.Interaction方法(例如InputBox()等)。

但是我的应用程序本身并未使用该程序集,因此该程序集永远不会加载。

因此,为了强制加载程序集,我最终将其简单地添加到了Form1_Load中:

if (DateTime.Now < new DateTime(1000, 1, 1, 0, 0, 0)) { // never happens
  Microsoft.VisualBasic.Interaction.Beep();
  // you can add more things here
}

编译器认为可能需要该程序集,但是实际上这永远不会发生。

这不是一个非常复杂的解决方案,但是又快又脏。

答案 9 :(得分:0)

我根据@Jon Skeet 答案创建了自己的答案,并带有名称前缀过滤以避免加载不必要的程序集:

public static IEnumerable<Assembly> GetProjectAssemblies(string prefixName)
{
    var assemblies = new HashSet<Assembly>
    {
        Assembly.GetEntryAssembly()
    };

    for (int i = 0; i < assemblies.Count; i++)
    {
        var assembly = assemblies.ElementAt(i);

        var referencedProjectAssemblies = assembly.GetReferencedAssemblies()
            .Where(assemblyName => assemblyName.FullName.StartsWith(prefixName))
            .Select(assemblyName => Assembly.Load(assemblyName));

        assemblies.UnionWith(referencedProjectAssemblies);
    }

    return assemblies;
}