У меня есть составное приложение ASP .NET MVC 3 Razor, использующее MEF. Все будет хорошо, если я разверну плагины в виде DLL-файлов и представлений (CSHTML) в обычной папке Views
из приложения. Но это не очень чисто, и это не будет настоящим плагином, если я не буду размещать представления как встроенные ресурсы в файлах DLL (вместе с контроллерами и моделями).
Я следил за многими статьями (большинство из них устарели). На самом деле здесь, в Stack Overflow, есть один неплохой пример: Контроллеры и представления внутри библиотеки классов.
Я также проверил документы для VirtualPathProvider
, и мне удалось создать собственный, который находит файл в сборке и загружает его идеально (или, по крайней мере, получает к нему поток). Для этого я следовал VirtualPathProvider
документация в MSDN.
Также существует реализация для VirtualFile, но пока нет для VirtualDirectory.
Вот в чем проблема. Я работаю с представлениями Razor. Я знаю, что им нужны спецификации конфигурации из файла web.config
для Razor, чтобы построить их. Но если я встрою их в DLL, эта конфигурация просто потеряется.
Интересно, поэтому я продолжаю получать ошибку:
Представление в «~/Plugins/CRM.Web.Views.CRM.Index.cshtml» должно быть производным от WebViewPage или WebViewPage.
Может быть, мне просто нужно добавить код, чтобы он работал? Любые идеи?
4 ответа
Мой предпочтительный способ встраивания Razor Views в библиотеку классов — скопировать их в папки Views/Areas веб-сайта MVC с событием после сборки. Расположение пользовательских представлений можно указать, если вы переопределяете ViewEngine или VirtualPathProvider.
Сложная часть для меня заключалась в том, чтобы заставить intellisense работать в этих библиотеках View Class. Во-первых, вы должны добавить Web.Config в свою сборку View. Обратите внимание, что вам не обязательно включать его в свою сборку. Он должен находиться только в корневом каталоге сборки (или в папке представлений). Вот пример. Обратите внимание на важный раздел Сборки/Компиляция.
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
</sectionGroup>
</configSections>
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
</namespaces>
</pages>
</system.web.webPages.razor>
<appSettings>
<add key="webpages:Enabled" value="false" />
</appSettings>
<system.web>
<compilation targetFramework="4.0">
<assemblies>
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</assemblies>
</compilation>
<httpHandlers>
<add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
</httpHandlers>
<!--
Enabling request validation in view pages would cause validation to occur
after the input has already been processed by the controller. By default
MVC performs request validation before a controller processes the input.
To change this behavior apply the ValidateInputAttribute to a
controller or action.
-->
<pages
validateRequest="false"
pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<controls>
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
</controls>
</pages>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<remove name="BlockViewHandler"/>
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
</system.webServer>
</configuration>
Затем вам нужно изменить файл vbproj вашей библиотеки классов, чтобы все элементы OutputPath указывали на «bin\», а не на «Debug\bin\» или «Release\bin\». Это основное различие, которое я обнаружил между библиотеками классов и типами веб-проектов ASP.Net, которые могут вызывать ошибки IntelliSense.
Если вы по-прежнему получаете сообщение об ошибке обязательного наследования, рассмотрите возможность использования @Inherits System.Web.Mvc.WebViewPage в своих представлениях. Если вы не копируете свои представления в проект своего веб-сайта, возможно, вы загружаете их из встроенных ресурсов с помощью пользовательского ViewEngine/VirtualPathProvider. Если это так, вам определенно нужны наследования, чтобы Razor, к сожалению, знал, какой у вас базовый класс представления.
Удачи.
Вы можете взглянуть на подписка на запись в блоге.
Хоссам,
Пост, о котором вы говорите, - это то, что Дарин уже предложил. Основным недостатком этого подхода является использование специального компилятора MvcRazorClassGenerator для преобразования файлов представления CSHTML в файлы классов. Для этого вам необходимо установить для каждого представления CSHTML в вашем проекте значение Content, а для пользовательского инструмента — MvcRazorClassGenerator.
Я не могу говорить за LordALMMa, но я скачал исходный код компилятора и попробовал его, и он не совсем работает так, как я надеялся.
Мой другой подход заключался в том, чтобы включить файлы CSHTML в качестве встроенных ресурсов во внешнюю DLL, прочитать необработанное содержимое файла и выполнить представление в виде строки (см. пример RazorEngine в CodeProject: http://razorengine.codeplex.com/)
Я не хотел полностью зависеть от RazorEngine в корпоративном приложении, потому что я не знаю, насколько хорошо он совместим со всем синтаксисом Razor, поэтому я пока отказался от этого.
Я исхожу из прототипа, созданного мной в ASP.NET MVC 2.0, который представляет собой многопользовательское приложение. На ферме серверов у нас есть один экземпляр приложения, где все клиенты используют одну и ту же кодовую базу. В моем прототипе MVC 2.0 я смог определить, для какого «клиента» был сделан запрос, проверить пользовательский контроллер, который переопределяет базовый (для настройки основного кода), а также проверить пользовательские представления (для настройки основной вид). Это позволяет нам развертывать «плагин» для каждого клиента. Программное обеспечение определяет, есть ли у клиента настраиваемый контроллер, который соответствует запросу, а также настраиваемое действие, которое соответствует, и если это так, оно использует вместо этого настраиваемый контроллер/действие.
Когда я начал переносить свой прототип на MVC 3, я столкнулся с той же проблемой, что и у LordALMMa: ошибка «Представление в '... Index.cshtml' должно быть получено из WebViewPage или WebViewPage». Я посмотрю, как разместить "@inherits System.Web.Mvc.WebViewPage" в своих представлениях CSHTML и посмотрю, приблизит ли это меня к тому, чтобы заставить его работать.
Поскольку у меня есть работающий прототип MVC 2.0, использующий MVC 3, Razor не является главным приоритетом, и я не трачу на него кучу времени. Я уверен, что смогу перенести MVC 2.0 в MVC 3.0, используя движок WebForms, если нам нужно использовать 4.0 Framework.
Эй, я подозреваю, что у вас есть веские причины для просмотра внутри DLL. Однако также учтите, что это необычный способ упаковки всего в одну сущность.
Если вы разрабатываете плагин, в наши дни люди выбирают упаковку в формате NUGET, который, среди прочего, также решает ваши проблемы. Он имеет структуру .nupkg, которая также является одним из способов распространения плагинов в виде пакетов и библиотек.
Другое решение, которому обычно следуют сообщества, заключается в том, что (если они не хотят чего-то столь же сложного, как nuget), они кодируют DLL-плагины таким образом, что он не использует механизмы просмотра, такие как бритва, вместо этого выводит HTML сам по себе, используя старый примитивный способ ответа. .Write и, таким образом, стать независимыми от файлов cshtml. Если вы все еще хотите использовать cshtml, см. эту запись в блоге для предварительной компиляции их в классы.
Похожие вопросы
Связанные вопросы
Новые вопросы
asp.net-mvc
ASP.NET MVC Framework - это платформа и инструмент веб-приложений с открытым исходным кодом, которые реализуют версию шаблона модель-представление-контроллер (MVC), адаптированную к веб-приложениям и построенную на основе технологии ASP.NET.