Платформа плагинов с ASP.NET MVC3 и встроенными представлениями Razor.

Я разрабатываю платформу плагинов для ASP.NET MVC3 с использованием представлений Razor, и у меня возникла проблема с корректной работой встроенных представлений.

Среда подключаемых модулей имеет следующие функции:

  • У каждого плагина есть свои модели, контроллеры и представления. Представления представляют собой встроенные ресурсы, а контроллеры наследуются от класса PluginController.
  • Плагины имеют ссылки на зависимость от общей библиотеки классов, которая определяет базовый класс PluginController.
  • Веб-приложение-оболочка, в котором размещены подключаемые модули, не должно содержать ссылок на какие-либо подключаемые модули во время разработки, поскольку во время разработки оно не знает, какие подключаемые модули у него есть.
  • DLL-файлы подключаемых модулей помещаются в папку в приложении оболочки, которая не является папкой /bin.
  • The shell takes care of:
    1. Discovering the plugins (using reflection)
    2. Регистрация всех контроллеров (для этого я использую Spring.Net)
    3. Создание маршрутов к контроллерам
    4. Обслуживание файлов Razor (cshtml) через настраиваемый VirtualPathProvider

Теперь все работает нормально, за исключением случаев, когда встроенные представления имеют ссылки на типы в подключаемой dll. Затем я получаю печально известную ошибку (имена опущены):

The type or namespace name '[Plugins]' does not exist in the namespace '[MyPluginSolution]' (are you missing an assembly reference?)

Причина этого в том, что компилятор csc, который вызывается во время выполнения для компиляции представлений razor, получает только ссылки на dll из папки bin и GAC.

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

Я мог бы, конечно, скинуть dll плагина в папку /bin, но мой вопрос:

Есть ли способ зарегистрировать библиотеки DLL в папке, отличной от bin (и не относящейся к GAC), и обращаться с ними как с «первоклассными гражданами», чтобы их можно было использовать в представлениях Razor?


person lasseschou    schedule 24.06.2011    source источник


Ответы (6)


Хорошо, решение было найдено с помощью этой статьи.

Сначала я создаю класс с PreApplicationStartMethod. Этот метод сканирует папку плагина и копирует библиотеки DLL в папку AppDomain.DynamicDirectory.

Затем каждая из этих dll загружается с помощью BuildManager.AddReferencedAssembly.

И вуаля, представления Razor со строгой типизацией прекрасно компилируются. Смотрите код здесь:

[assembly: PreApplicationStartMethod(typeof(MySolution.PluginHandler.PluginActivator), "Initialize")]
namespace MySolution.PluginHandler
{
    public class PluginActivator
    {
        private static readonly DirectoryInfo PluginFolderInfo;

        static PluginActivator() {
            PluginFolderInfo = new DirectoryInfo(HostingEnvironment.MapPath("~/plugins"));
        }

        public static void Initialize() {
            CopyPluginDlls(PluginFolderInfo, AppDomain.CurrentDomain.DynamicDirectory);
            LoadPluginAssemblies(AppDomain.CurrentDomain.DynamicDirectory);
        }

        private static void CopyPluginDlls(DirectoryInfo sourceFolder, string destinationFolder)
        {
            foreach (var plug in sourceFolder.GetFiles("*.dll", SearchOption.AllDirectories)) {
                if (!File.Exists(Path.Combine(destinationFolder, plug.Name))) {
                    File.Copy(plug.FullName, Path.Combine(destinationFolder, plug.Name), false);
                }
            }
        }

        private static void LoadPluginAssemblies(string dynamicDirectory)
        {
            foreach (var plug in Directory.GetFiles(dynamicDirectory, "*.dll", SearchOption.AllDirectories)) {
                Assembly assembly = Assembly.Load(AssemblyName.GetAssemblyName(plug));
                BuildManager.AddReferencedAssembly(assembly);
            }
        }
    }
}

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

person lasseschou    schedule 27.06.2011
comment
Причина того, что плагины не помещаются в каталог bin, заключается в том, что вы не хотите, чтобы приложение перезапускалось? это решение загружает плагин только при запуске приложения (BuildManager.AddReferencedAssembly). Можно ли динамически ссылаться на сборку в dynamicDirectory? - person Anthony Johnston; 06.09.2011
comment
Причина в том, что это решение может иметь потенциально большое количество плагинов, каждый с 3-5 dll. И поскольку вы не можете поместить их в подпапки в /bin, предпочтительным решением было использовать для этого другую папку. - person lasseschou; 07.09.2011

Дэвид Эббо недавно написал в блоге о предварительной компиляции представлений Razor в сборки. Вы можете просмотреть сообщение здесь.

Вы должны быть в состоянии избежать регистрации сборок напрямую, динамически загружая сборки (я обычно использую для этого свой контейнер IoC), а затем вызывая BuildManager.AddReferencedAssembly для каждой сборки плагина.

person Ben Foster    schedule 24.06.2011
comment
Спасибо, Бен. Но в его случае, как и в случае с Крисом ван де Стигом, хост-приложение имеет прямую ссылку на сборку плагина, так что, к сожалению, мою проблему это не решает. - person lasseschou; 24.06.2011
comment
Я обновил свой ответ, но учтите, что это все в теории :) - подробнее haacked.com/archive/2010/05/16/ - person Ben Foster; 24.06.2011
comment
Привет, Бен, твое последнее редактирование указало мне правильное направление. Решение состояло в том, чтобы включить метод pre-Application_Start, который копирует все DLL-плагины в AppDomain.DynamicDirectory, а затем вызывает BuildManager.AddReferencedAssembly(). В этой статье показано, как это сделать как при полном доверии, так и при среднем доверии: shazwazza.com/post/. Я сам опубликую ответ здесь. - person lasseschou; 27.06.2011

Проверьте МЭФ

Вы также можете сделать это с помощью Windsor Installers — Майк Хэдлоу хорошо потрудился над этим: http://mikehadlow.blogspot.com/2010/10/experimental-aspnet-mvc-add-ins-updated.html

person Community    schedule 24.06.2011

Пожалуйста, взгляните на исходный код NOPCommerce. У него хорошая структура плагинов, основанная на работе Шеннон< /а>.

person ATechieThought    schedule 11.03.2013

Вы также можете использовать функцию «Область» в MVC3 и 4, вы можете создать хорошую систему плагинов. Это дает разделение обработки модели плагина, представления и контроллера в его собственной сборке.

person ATechieThought    schedule 28.03.2013

Вы можете найти это полезным http://www.adverseconditionals.com/2011/07/portable-aspnet-code-using.html

Я начал создавать два проекта

МояБиблиотекаМояБиблиотека.Шаблоны

и иметь представления как содержимое в .Templates и добавлять в виде ссылок и устанавливать для EmbeddedResource в MyLibrary. Люди, которые хотят переопределить представления, могут установить проект .Templates.

person mcintyre321    schedule 17.05.2012