ASP.NET - AppDomain.CurrentDomain.GetAssemblies () - сборки отсутствуют после перезапуска AppDomain - PullRequest
30 голосов
/ 24 августа 2010

У меня есть Bootstrapper, который просматривает все сборки в приложении ASP.NET MVC, чтобы найти типы, которые реализуют интерфейс IBootstrapperTask, а затем регистрирует их с помощью IOC Contrainer.Идея состоит в том, что вы можете буквально размещать свои задания IBootstrapper в любом месте и организовывать свои проекты так, как вам удобно.

Код для 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 в режиме интегрированного конвейера в 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();
    }
}

... кажется, это решает проблему, но это немного взломать.

Ответы [ 2 ]

50 голосов
/ 14 сентября 2010

Я просмотрел исходный код 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;
    }
}
1 голос
/ 24 августа 2010

Похоже, вы решили свою проблему.

Изменить: Лично я бы на самом деле перечислять сборки, одну за другой, загружать их и искать интерфейс Делайте это на основе файлов, а не на том, что делает домен приложений.

...