【问题标题】:ASP.NET - AppDomain.CurrentDomain.GetAssemblies() - Assemblies missing after AppDomain restartASP.NET - AppDomain.CurrentDomain.GetAssemblies() - AppDomain 重新启动后缺少程序集
【发布时间】:2010-08-23 22:34:30
【问题描述】:

我有一个引导程序,它查看 ASP.NET MVC 应用程序中的所有程序集以查找实现 IBootstrapperTask 接口的类型,然后将它们注册到 IOC 容器。这个想法是您可以将您的 IBootstrapperTasks 放在任何地方,并按照您的喜好组织您的项目。

引导程序代码:

public class Bootstrapper
{
    static Bootstrapper()
    {
        Type bootStrapperType = typeof(IBootstrapperTask);

        IList<Assembly> assemblies = AppDomain.CurrentDomain.GetAssemblies();

        List<Type> tasks = new List<Type>();

        foreach (Assembly assembly in assemblies)
        {
            var types = from t in assembly.GetTypes()
                        where bootStrapperType.IsAssignableFrom(t)
                            && !t.IsInterface && !t.IsAbstract
                        select t;

            tasks.AddRange(types);
        }

        foreach (Type task in tasks)
        {
            if (!IocHelper.Container().Kernel.HasComponent(task.FullName))
            {
                IocHelper.Container().AddComponentLifeStyle(
                    task.FullName, task, LifestyleType.Transient);
            }
        }
    }

    public static void Run()
    {
        // Get all registered IBootstrapperTasks, call Execute() method
    }
}

完整构建后,AppDomain.CurrentDomain.GetAssemblies() 返回我的解决方案中的所有程序集(包括所有 GAC 的程序集,但这不会打扰我)。

但是,如果 AppDomain 重新启动,或者我“反弹”Web.Config 文件(添加空格并保存),静态构造函数会再次运行,但是当调用 AppDomain.CurrentDomain.GetAssemblies() 时,大多数程序集丢失了,包括包含我的 IBootstrapperTask 类型的那个。

如何解决这个问题?我想我可以 System.IO /bin 目录并手动加载其中的所有 DLL,但如果可能的话宁愿避免这种情况,或者这是唯一的方法吗?我是否采取了正确的通用方法?

这是一个在 .NET 4.0 上运行的 ASP.NET MVC 2.0 应用程序,我使用内置的 Visual Studio 2010 Cassini Web 服务器和 Windows Server 2008 上的集成管道模式下的 IIS7.0 遇到了这个问题。


编辑:我刚刚看到这篇 SO 帖子 Difference between AppDomain.GetAssemblies and BuildManager.GetReferencedAssemblies,它说 AppDomain 仅在需要时加载程序集(例如,当第一次调用该程序集的方法/类时) .我想这可以解释为什么 AppDomain.CurrentDomain.GetAssemblies() 上缺少程序集,因为 Bootstrapper 很早就运行了。

我注意到如果我在引导程序之前从缺少的程序集中调用了“某物”,例如:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        MyApp.MissingAssembly.SomeClass someClass =
            new MyApp.MissingAssembly.SomeClass();

        Bootstrapper.Run();
    }
}

...它似乎解决了这个问题,但它是一个有点黑客。

【问题讨论】:

  • 很好的问答!参观了你的博客——你写了一些好东西。

标签: c# asp.net asp.net-mvc reflection assemblies


【解决方案1】:

我查看了 ASP.NET MVC 2.0 源代码并查看了AreaRegistration.RegisterAllAreas(); 是如何实现的。此行通常放入 Global.asax Application_Start() 方法中,并在内部扫描所有程序集以查找实现 AreaRegistration 抽象类型的类型。这有点像我所追求的行为。

RegisterAllAreas() 似乎调用了BuildManager.GetReferencedAssemblies(),如果它对 MVC 来说足够好,那么它对我来说就足够好了:-)

我已经做了一些实验,即使没有对 Visual Studio 解决方案中任何项目的引用,BuildManager.GetReferencedAssemblies() 甚至会拾取放入 /bin 文件夹中的临时随机 DLL。所以它看起来比AppDomain.Current.GetAssemblies()可靠得多。

我已将我的程序集定位器代码重写为以下内容:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Compilation;

public static class AssemblyLocator
{
    private static readonly ReadOnlyCollection<Assembly> AllAssemblies;
    private static readonly ReadOnlyCollection<Assembly> BinAssemblies;

    static AssemblyLocator()
    {
        AllAssemblies = new ReadOnlyCollection<Assembly>(
            BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToList());

        IList<Assembly> binAssemblies = new List<Assembly>();

        string binFolder = HttpRuntime.AppDomainAppPath + "bin\\";
        IList<string> dllFiles = Directory.GetFiles(binFolder, "*.dll",
            SearchOption.TopDirectoryOnly).ToList();

        foreach (string dllFile in dllFiles)
        {
            AssemblyName assemblyName = AssemblyName.GetAssemblyName(dllFile);

            Assembly locatedAssembly = AllAssemblies.FirstOrDefault(a =>
                AssemblyName.ReferenceMatchesDefinition(
                    a.GetName(), assemblyName));

            if (locatedAssembly != null)
            {
                binAssemblies.Add(locatedAssembly);
            }
        }

        BinAssemblies = new ReadOnlyCollection<Assembly>(binAssemblies);
    }

    public static ReadOnlyCollection<Assembly> GetAssemblies()
    {
        return AllAssemblies;
    }

    public static ReadOnlyCollection<Assembly> GetBinFolderAssemblies()
    {
        return BinAssemblies;
    }
}

【讨论】:

  • 我遇到了这个问题并使用了您的解决方案。投票!谢谢!
  • 非常感谢!我也一直卡在这个问题上。你节省了我的时间。
  • 不错的选择,但尝试在单元测试中使用它会出现异常“无法在应用程序的预启动初始化阶段调用此方法。”
【解决方案2】:

看起来您解决了自己的问题。

编辑:就我个人而言,我实际上会一一列举程序集,加载它们并查找接口。基于文件而不是 AppDomain 正在做什么。

【讨论】:

  • 你的意思是最后一个代码示例 MyApp.MissingAssembly.SomeClass 等吗?如果是这样,这不是一个真正的解决方案,我必须在 Bootstrapper.Run() 之前继续向不同的程序集添加虚拟调用。
  • 我认为他的意思是您应该浏览文件,明确加载它们。这样你就不会依赖于你是否碰巧在调用中加载了它。
猜你喜欢
  • 2012-11-04
  • 1970-01-01
  • 2016-10-27
  • 1970-01-01
  • 1970-01-01
  • 2018-12-13
  • 1970-01-01
  • 2015-07-27
  • 2016-09-18
相关资源
最近更新 更多