ASP.NET - AppDomain.CurrentDomain.GetAssemblies() - AppDomain重新启动后丢失的程序集

Sun*_*oot 30 c# asp.net reflection asp.net-mvc assemblies

我有一个Bootstrapper,它可以查看ASP.NET MVC应用程序中的所有程序集,找到实现IBootstrapperTask接口的类型,然后使用IOC Contrainer注册它们.我们的想法是,您可以随意将您的IBootstrapperTasks放置在任何地方,并按您喜欢的方式组织您的项目.

Bootstrapper代码:

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

完整构建后,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 post AppDomain.GetAssemblies和BuildManager.GetReferencedAssemblies之间区别,它说AppDomain只在需要时加载程序集(例如,当首次调用该程序集中的方法/类时).我想这可以解释为什么AppDomain.CurrentDomain.GetAssemblies()由于Bootstrapper很早就开始运行而导致大会缺失.

我注意到,如果我在Bootstrapper之前调用缺少的Assembly中的'something',例如:

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

        Bootstrapper.Run();
    }
}
Run Code Online (Sandbox Code Playgroud)

......似乎解决了这个问题,但这有点像黑客攻击.

Sun*_*oot 51

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

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

我已经完成了一些实验,BuildManager.GetReferencedAssemblies()甚至会将随机DLL放入/ bin文件夹中,即使没有引用Visual Studio解决方案中的任何项目也是如此.所以看起来比它更可靠AppDomain.Current.GetAssemblies().

我已将Assembly定位器代码重写为以下内容:

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