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

Dan*_*fer 72 c# assemblies appdomain

我的项目设置如下:

  • 项目"定义"
  • 项目实施"
  • 项目"消费者"

项目"消费者"引用"定义"和"实现",但不静态引用"实现"中的任何类型.

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

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

Dan*_*fer 80

这似乎可以解决问题:

        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))));
Run Code Online (Sandbox Code Playgroud)

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


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

  • 什么设施?我没有通过搜索找到任何东西. (9认同)
  • 使用MEF,上面的代码可以简化为:`new DirectoryCatalog(".");`(需要引用`System.ComponentModel.Composition`). (4认同)
  • 这对我来说不起作用,我引用的程序集,没有加载,不会出现在AppDomain.CurrentDomain.GetAssemblies()中......嗯...... (3认同)
  • 这个答案解决了我的问题并为我工作。我有一个 MS 单元测试项目引用我的另一个程序集,并且 AppDomain.CurrentDomain.GetAssemblies() 在运行测试时没有返回该程序集。我怀疑,即使我的单元测试使用的是该库中的代码,程序集也可能不会出现在“GetAssemblies”中,因为与运行常规程序相比,vs.net 加载 MS 单元测试项目(类库)的方式.exe 应用程序。如果您的代码使用反射并且单元测试失败,请记住一些事情。 (2认同)
  • 只是要添加,请注意动态加载的程序集动态程序集不支持被调用的成员。筛选掉IsDynamic = false的程序集,或者如果您可以容忍负载,请尝试/捕获对CurrentDomain.Load的调用。还有`assembly.Location`。那一个也需要检查。对于`IsDynamic`程序集无效。 (2认同)

Jon*_*eet 55

您可以使用Assembly.GetReferencedAssemblies获取AssemblyName[],然后调用Assembly.Load(AssemblyName)每个.当然,你需要递归 - 但最好跟踪你已经加载的装配:)

  • 这种情况下,GetReferencedAssemblies显然会返回一个"优化"列表 - 因此,如果您没有在引用的程序集中显式调用代码,则不会包含它.(根据[本讨论](https://social.msdn.microsoft.com/Forums/vstudio/en-US/17f89058-5780-48c5-a43a-dbb4edab43ed/getreferencedassemblies-not-returning-complete-list?forum=netfxbcl )) (5认同)
  • 如果您在加载参考装配后,以上将解决您的问题.您也可以询问特定类型typeof(T).如果有帮助,请提供组件.我觉得你需要的是动态加载包含实现的程序集(未引用).如果是这种情况,则必须保留静态名称列表并手动加载它们或遍历整个目录,加载然后找到具有正确接口的类型. (4认同)
  • 在正常情况下,这可能是正确的,但是当使用 DI 容器发现可用服务(通过“System.Reflection”)时,它自然不会找到尚未加载的程序集中包含的服务。从那以后,我的默认方法是从我的应用程序的 CompositionRoot 中每个引用程序集的随机类型创建一个虚拟子类,以确保所有依赖项都到位。我希望我可以通过预先加载所有内容来跳过这些废话,即使以进一步增加启动时间为代价。@JonSkeet 还有其他方法可以做到这一点吗?谢谢 (3认同)

jme*_*hus 21

只是想分享一个递归的例子.我在我的启动例程中调用LoadReferencedAssembly方法,如下所示:

foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
    this.LoadReferencedAssembly(assembly);
}
Run Code Online (Sandbox Code Playgroud)

这是递归方法:

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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我想知道循环程序集引用是否会导致堆栈溢出异常被抛出. (4认同)

Mei*_*hes 14

如果您使用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)));
Run Code Online (Sandbox Code Playgroud)