Событие AssemblyResolve не запускается во время компиляции динамической сборки для страницы aspx - PullRequest
3 голосов
/ 16 мая 2010

Этот действительно меня бесит. Здесь идет:

Моя цель - загружать сборки во время выполнения, которые содержат встроенные aspx, ascx и т. Д. Я также хотел бы не блокировать файл сборки на диске, чтобы я мог обновлять его во время выполнения без перезапуска приложения. (Я знаю, что предыдущая версия будет загружена).

С этой целью я написал провайдера виртуального пути, который добился цели. Я подписался на событие CurrentDomain.AssemblyResolve, чтобы перенаправить каркас на мои сборки.

Проблема в том, что когда платформа пытается скомпилировать динамическую сборку для страницы aspx, я получаю следующее:

Сообщение об ошибке компилятора: CS0400: не удалось найти тип или имя пространства имен 'Pages' в глобальном пространстве имен (отсутствует ссылка на сборку?)

Ошибка источника: открытый класс app_resource_pages__version_1_0_0_0__culture_neutral__publickeytoken_null_default_aspx: global :: Pages._Default, System.Web.SessionState.IRequiresSessionState, System.Web.IHttpHandle

Я заметил, что если я загружаю сборку с помощью Assembly.Load (AssemblyName) или Assembly.LoadFrom (имя файла), я не получаю вышеуказанную ошибку. Если я загружаю его с помощью Assembly.Load (byte []) (чтобы не блокировать его), генерируется исключение, но мой обработчик AssemblyResolve при вызове возвращает сборку правильно (она вызывается один раз).

Так что я предполагаю, что он вызывается один раз, когда каркас анализирует разметку asp, но не когда он пытается создать динамическую сборку для страницы aspx.

Ответы [ 2 ]

4 голосов
/ 17 мая 2010

Я не уверен, что является причиной отсутствия ссылки на сборку, но если мы отступим немного назад и перейдем к точке, в которой ваша программа работает должным образом, то нам придется решить другую проблему. Эта проблема заключается в блокировке загруженной сборки. .Net Framework всегда блокирует загруженные сборки. Причина, по которой вы можете обновить dll-файлы внутри папки bin, на самом деле - уловка. Видите ли, у AppDomain есть замечательное свойство, называемое ShadowCopyDirectories, которое определяет каталоги, которые будут копироваться тенями при загрузке сборки. Таким образом, изменив список теневых копий каталогов, вы можете загрузить их из любой папки, не блокируя сборки:

    protected const string ApplicationAssembliesFolder = "~/Assemblies";

    protected void Application_Start(object sender, EventArgs e)
    {
        var assembliesPath = Server.MapPath(ApplicationAssembliesFolder);

        AppDomain.CurrentDomain.SetShadowCopyPath(
            AppDomain.CurrentDomain.SetupInformation.ShadowCopyDirectories + 
            Path.PathSeparator + assembliesPath);

        Assembly.LoadFrom(
            Path.Combine(assembliesPath, "Example.dll"));
    }
1 голос
/ 18 мая 2010

Я думаю, что это работает со следующим:

 public Assembly GetAssembly()
    {
        Assembly result = cache.Get(assemblyKey) as Assembly;

        if (result == null)
        {
            lock (this)
            {
                result = cache.Get(assemblyKey) as Assembly;
                if (result == null)
                {
                    assemblyName = System.Reflection.AssemblyName.GetAssemblyName(assemblyFile);
                    result = Assembly.Load(assemblyName);
                    cache.Add(assemblyKey, result, new CacheDependency(assemblyFile), Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.High, new CacheItemRemovedCallback(OnAssemblyRemoved));
                }
            }
        }
        return result;
    }

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

Подведем итог:

  1. AppDomain.CurrentDomain.SetShadowCopyPath
  2. Assembly.Load (AssemblyName.GetAssemblyName (assemblyFile))
...