Подтвердить что ты не робот

ASP.NET - AppDomain.CurrentDomain.GetAssemblies() - Ассембли отсутствуют после перезапуска AppDomain

У меня есть 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
    }
}

После полной сборки AppDomain.CurrentDomain.GetAssemblies() возвращает все сборки в моем решении (включая все GAC, но это меня не беспокоит).

Однако, если AppDomain перезагружается или я отказываюсь от файла Web.Config(добавляя пробел и сохранение), статический конструктор запускается снова, но когда вызывается AppDomain.CurrentDomain.GetAssemblies(), большинство сборок отсутствуют, включая те, которые содержат мои типы IBootstrapperTask.

Как мне обойти эту проблему? Думаю, я мог бы использовать каталог System.IO в каталоге /bin и загружать все библиотеки DLL там вручную, но предпочел бы избежать этого, если это возможно, или это единственный способ? Я принимаю правильный общий подход к этому?

Это приложение ASP.NET MVC 2.0, работающее на .NET 4.0, я получаю эту проблему со встроенным веб-сервером Visual Studio 2010 Cassini и с IIS7.0 в режиме Integrated Pipeline Mode на Windows Server 2008.


Изменить: Я только что наткнулся на эту публикацию SO после Разница между AppDomain.GetAssemblies и BuildManager.GetReferencedAssemblies, в которой говорится, что AppDomain загружает только сборки поскольку они необходимы (например, когда сначала вызывается метод/класс из этой Ассамблеи). Я предполагаю, что это объясняет, почему Ассамблей не хватает на AppDomain.CurrentDomain.GetAssemblies(), поскольку Bootstrapper запускается очень рано.

Я заметил, что если я поместил вызов "что-то" из отсутствующей сборки перед Bootstrapper, например:

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

        Bootstrapper.Run();
    }
}

... похоже, проблема устранена, но это немного взломан.

4b9b3361

Ответ 1

Я просмотрел исходный код ASP.NET MVC 2.0 и посмотрел, как реализуется AreaRegistration.RegisterAllAreas();. Эта строка обычно помещается в метод Global.asax Application_Start() и внутренне проверяет все сборки для типов, реализующих абстрактный тип AreaRegistration. Это своего рода поведение, которое я испытываю после.

Кажется, RegisterAllAreas() делает вызов BuildManager.GetReferencedAssemblies(), ну, если он достаточно хорош для MVC, тогда он достаточно хорош для меня: -)

Я провел некоторое экспериментирование, и BuildManager.GetReferencedAssemblies() даже заберет adhoc, случайную DLL, упавшую в папку /bin, даже без ссылок на какие-либо проекты в решении Visual Studio. Таким образом, он кажется гораздо более надежным, чем 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.