Почему ASP.NET разрешает ссылки на сборки по-разному?

Я действительно изо всех сил пытался найти похожую проблему, чтобы получить некоторые зацепки, но, похоже, никто не описывает случай, который у нас есть, так что вот оно.

Фон

У нас есть продукт со следующим общим дизайном:

[Локальная папка установки]

  • Содержит набор сборок .NET, реализующих большую часть функциональности нашего продукта.
  • Пример: Implementation1.dll, Implementation2.dll

[GAC]

  • ClientAPI.dll. Наша клиентская сборка, на которую будут ссылаться конечные пользовательские проекты Visual Studio. Имеет сильные ссылки на DLL реализации в локальной папке установки.

В ClientAPI.dll у нас есть точка входа, которую должны вызывать проекты конечных пользователей. Назовем его Initialize().

Самое первое, что мы делаем в Initialize, — это устанавливаем так называемый обработчик разрешения сборки в текущем домене, используя событие AssemblyResolve. Этот обработчик будет знать, как найти DLL реализации и загрузить их в клиентский процесс, используя Assembly.Load().

Рассмотрим консольное приложение. Это будет выглядеть примерно так:

class Class1
{
    void Main(string[] args)
    {
        ClientAPI.Initialize();

        // Use other API's in the assembly, possibly internally referencing the
        // implementation classes, that now will be resolved by our assembly
        // resolve handler.
    }
 }

Теперь все хорошо в мире консоли/Windows Forms/WPF. Наш обработчик разрешения сборки правильно установлен и вызывается, и он может успешно разрешать ссылки на DLL реализации, как только ClientAPI.dll потребует их функциональности.

Постановка задачи

При этом мы не собираемся поддерживать только консольные или WPF-приложения, поэтому мы полагались на тот же дизайн в ASP.NET. Следовательно, создавая новый проект веб-приложения ASP.NET в VS 2010, мы решили, что все будет так же просто, как:

class Globals : HttpApplication
{
    void Application_Start(object sender, EventArgs e)
    {
        ClientAPI.Initialize();

        // ...
    }
}

Несколько 20-30 часов пребывания во вселенной времени выполнения ASP.NET, попробовав все вышеперечисленное как на сервере разработки, так и в IIS, мы узнали, что на самом деле все не так, как мы ожидали.

Оказывается, в ASP.NET, как только на класс ClientAPI ссылаются где-либо, все ссылки, которые он имеет на любые другие сборки, мгновенно разрешаются. И не только это: результаты кэшируются (преднамеренно, начиная с .NET 2.0, который мы нашли), а это означает, что у нас никогда не будет шанса попытаться помочь CLR.

Без дальнейших подробностей о различных вещах, которые мы попробовали и узнали, в основном это сводится к следующему вопросу, который у нас есть:

Почему ASP.NET разрешает ссылки именно так? Это несовместимо с тем, как это делают другие типы приложений, и даже более того, это не соответствует документации .NET/среды выполнения CLR, указывающей, что ссылки на внешние типы/сборки должны разрешаться при первой необходимости (т.е. когда впервые используется в коде).

Любое понимание/идеи будут высоко оценены!


person Per Samuelsson    schedule 11.08.2011    source источник
comment
отличный вопрос. нужен какой-то толчок.   -  person Sky Sanders    schedule 08.02.2012
comment
Я тоже недавно столкнулся с этой проблемой. Это очень расстраивает, и нет действительно хорошего решения. Однако в ASP.NET 4 теперь существует одно решение в виде атрибута PreApplicationStartMethodAttribute, который позволяет запускать код на самом раннем этапе конвейера ASP.NET, что позволяет вам управлять разрешением ваших сборок. Проект WebActivator, доступный в NuGet, использует этот новый атрибут, а также расширяет его. Конечно, это работает только для ASP.NET 4... Дополнительную информацию можно найти здесь: haacked.com/archive/2010/05/16/   -  person Randy Burden    schedule 27.03.2012


Ответы (1)


Приложения Windows Forms/WPF запускаются на отдельных клиентских компьютерах (и, следовательно, запускаются в одном локальном контексте), тогда как ASP.Net запускается в IIS, в пуле приложений, на сервере или наборе серверов (в ситуации с веб-фермой). Все, что загружено в пул приложений, доступно для всего приложения (и, следовательно, совместно используется всеми клиентами, которые подключаются к приложению).

HttpApplication.Application_Start выполняется один раз при запуске приложения. Он не выполняется для каждого клиента, как это было бы с приложением Winforms - если вам нужно что-то инициализировать для каждого подключающегося клиента, используйте Session_Start или Session_OnStart, но тогда вы можете столкнуться с проблемами памяти на сервере, в зависимости от количества клиентов собираются подключиться к вашему веб-приложению. Это также зависит от того, является ли ваш класс одноэлементным, и является ли метод Initialize() статическим. Если у вас есть какая-либо из этих ситуаций, вы довольно быстро столкнетесь с проблемами межпоточности.

Кроме того, стоит отметить, что бездействующий пул приложений IIS через некоторое время перезагрузится. Например, если никто не использует веб-приложение в течение ночи, IIS очистит пул приложений приложения и освободит память. Эти параметры можно изменить в рамках администрирования IIS, но при этом следует соблюдать осторожность — изменение этих параметров для обхода плохо спроектированного объекта (или объекта, не предназначенного для веб-приложения) может создать еще больше проблем.

К вашему сведению — я немного суетлив, но во избежание сомнений, объект не кэшируется — да, он загружается в память, но то, как память управляется, зависит от того, как вы спроектировали объект (кэширование в веб-мире — это совершенно другое, и его можно реализовать на многих разных уровнях вашего приложения).

Не пытайтесь заставить веб-приложение работать как приложение Windows; вы только создадите себе больше проблем!

person Spikeh    schedule 10.02.2012