Ошибка анализатора: не удалось загрузить тип «x» из домена приложения из виртуального каталога в IIS7 - PullRequest
1 голос
/ 04 августа 2010

Так что мои сценарии немного смешные, но есть причина для этого.

У меня есть родительское веб-приложение с именем Parent и второе веб-приложение с именем Child.Дочерний объект - это виртуальный каталог в IIS7 в разделе «Родитель», который является приложением в IIS.Child не является дочерним каталогом parent в файловой системе, только в IIS как виртуальный каталог.При загрузке приложения (Application_Start в global.asax) в родительском веб-приложении я говорю ему загружать дочерние веб-библиотеки из папки bin ребенка, используя Assembly.LoadFrom (), загружая их в домен приложения Parent.Затем, когда я пытаюсь посетить /Child/Default.aspx, я получаю сообщение об ошибке:

Ошибка парсера

Сообщение об ошибке парсера: не удалось загрузить тип 'Child._Default '.

Теперь Child.dll (веб-библиотека, содержащая дочерний код и т. Д.) Находится в домене приложения родительского приложения, и я могу успешно отразить его и его элементы из кода позадина родительской странице Default.aspx.

Кроме того, на Child / Default.aspx, если я изменю Inherits = "Child._Default" на Inherits = "System.Web.UI.Page", а затем в <%Теги%> на странице перечисляют dll в домене приложения, который я вижу Child.dll и отражаем его членов и вызываем функции.

Одна вещь, которая работает, это изменение CodeBehind на CodeFile в директиве страницы.Затем страница анализируется правильно.Однако это работает только тогда, когда веб-сайты находятся в некомпилированном, неопубликованном виде.

1 Ответ

2 голосов
/ 04 августа 2010

Что происходит, так это то, что домен приложения не просматривается в списке сборок, когда он пытается разрешить «дочернюю» сборку для страницы в дочернем проекте.

Что вам нужно сделать, это использовать обработчик событий AssemblyResolve в AppDomain. Вы можете сделать это так:

Сначала мы создаем класс AssemblyLoader:

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

namespace Parent
{
    internal class AssemblyLoader
    {
        private static List<AssemblyInformation> virtualDirectoryAssemblies = new List<AssemblyInformation>();
        private static readonly string virtualDirectoryBinFolderFormatString = "~/{0}/bin/";
        private static readonly string[] pathSplitParams = new string[1] { "\\" };
        private static readonly string[] assemblyNameSplitParams = new string[1] { "," };

        internal static Assembly AssemblyResolve(object sender, ResolveEventArgs e)
        {
            var name = e.Name.Split(assemblyNameSplitParams, StringSplitOptions.RemoveEmptyEntries).First();
            if (!virtualDirectoryAssemblies.Exists(a => a.Name.Equals(name)))
                return null;

            return Assembly.LoadFrom(virtualDirectoryAssemblies.Single(a => a.Name.Equals(name)).Path);
        }

        internal static void LoadVirtualDirectories(List<string> virtualDirectories)
        {
            foreach (var v in virtualDirectories)
            {
                var path = HttpContext.Current.Server.MapPath(string.Format(virtualDirectoryBinFolderFormatString, v));
                AppDomain.CurrentDomain.AppendPrivatePath(path);
                AppDomain.CurrentDomain.SetShadowCopyPath(path);

                var assemblies = Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories).ToList();
                foreach (var a in assemblies)
                {
                    var name = a.Split(pathSplitParams, StringSplitOptions.RemoveEmptyEntries).Last().Replace(".dll", string.Empty);
                    if(!virtualDirectoryAssemblies.Exists(i => i.Name.Equals(name)))
                    {
                        virtualDirectoryAssemblies.Add(new AssemblyInformation
                        {
                            Name = name,
                            Path = a
                        });
                    }
                }
            }
        }

        class AssemblyInformation
        {
            public string Name { get;set; }
            public string Path { get; set; }
        }
    }
}

В файле web.config для родительского проекта я добавил это (если у вас больше виртуальных каталогов, идея состоит в том, чтобы иметь список с разделителями-запятыми):

<appSettings>
    <add key="VirtualDirectories" value="Child"/>
</appSettings>

В файле web.config дочернего проекта вы добавляете эту ссылку в сборку дочерней сборки:

<system.web>
    <compilation>
        <assemblies>
            <add assembly="Child, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
        </assemblies>
    </compilation>
</system.web>

Это также может быть так:

<system.web>
    <compilation>
        <assemblies>
            <add assembly="Child"/>
        </assemblies>
    </compilation>
</system.web>

Теперь, последнее, но не менее важное, мы помещаем это в Global.asax:

    protected void Application_Start(object sender, EventArgs e)
    {
        AppDomain.CurrentDomain.AssemblyResolve += AssemblyLoader.AssemblyResolve;

        var virtualDirectories = 
            ConfigurationManager.AppSettings.Get("VirtualDirectories").Split(new string[1] { "," }, StringSplitOptions.RemoveEmptyEntries).ToList();

        AssemblyLoader.LoadVirtualDirectories(virtualDirectories);
    }

И мы закончили ...: P

...