Виды MEF и Razor внутри библиотеки классов - PullRequest
28 голосов
/ 15 февраля 2011

У меня есть составное приложение ASP .NET MVC 3 Razor, использующее MEF. Все идет хорошо, если я хочу развернуть плагины как файлы DLL и представления (CSHTML) в обычной папке Views из приложения. Но это не очень чисто, и это не будет настоящий плагин, если я не помещу представления как встроенные ресурсы в файлы DLL (вместе с контроллерами и моделями).

Я следил за многими статьями (большинство из них устарели). На самом деле, здесь есть один неплохой пример переполнения стека: Контроллеры и представления внутри библиотеки классов

Я также проверил документы на VirtualPathProvider, и мне удалось создать пользовательский документ, который находит файл в сборке и прекрасно загружает его (или, по крайней мере, получает поток к нему). Для этого я следовал документации VirtualPathProvider на MSDN .

Существует также реализация для VirtualFile, но еще не для VirtualDirectory.

Вот проблема. Я работаю с Razor Views. Я знаю, что им нужны спецификации конфигурации из файла web.config, чтобы Razor мог их создать. Но если я встраиваю их в DLL, эта конфигурация просто теряется.

Интересно, вот почему я продолжаю получать ошибку:

Представление в '~ / Plugins / CRM.Web.Views.CRM.Index.cshtml' должно быть получено из WebViewPage или WebViewPage.

Может быть, мне просто нужно добавить код, чтобы он работал? Есть идеи?

Ответы [ 4 ]

7 голосов
/ 23 ноября 2011

Мой предпочтительный способ встроить Razor Views в библиотеку классов - скопировать их в папки Views / Areas на веб-сайте MVC с событием после сборки. Пользовательские местоположения просмотра могут быть указаны, если вы переопределяете ViewEngine или VirtualPathProvider.

Самое сложное для меня - заставить intellisense работать в этих библиотеках View Class. Во-первых, вы должны добавить Web.Config в вашу сборку View. Обратите внимание, что вам не нужно включать его в свою сборку. Он должен быть только в корневом каталоге сборки (или в папке views). Вот пример. Обратите внимание на важный раздел Сборки / Компиляция.

<?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. Если это так, вам определенно нужны Inherits, чтобы Razor, к сожалению, знал, каков ваш базовый класс представлений.

Удачи.

3 голосов
/ 15 февраля 2011

Вы можете взглянуть на следующую запись в блоге .

2 голосов
/ 25 мая 2011

Hossam

Пост, о котором вы говорите, - это то, что Дарин уже предложил. Основным недостатком этого подхода является использование собственного компилятора MvcRazorClassGenerator для преобразования файлов представления CSHTML в файлы классов. Для этого вам нужно установить для каждого представления CSHTML в вашем проекте значение Content и установить для Custom Tool значение 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.

0 голосов
/ 23 ноября 2011

Эй, я подозреваю, что у вас есть веские причины для желаний просмотра внутри DLL. Однако также учтите, что это необычный способ объединить все в одну сущность.

Если вы разрабатываете плагин, в наши дни люди выбирают упаковку в формате NUGET, что также решает ваши проблемы среди прочего. Он имеет структуру .nupkg, которая также является одним из способов распространения плагинов в виде пакетов и библиотек.

Другое решение, за которым обычно следуют сообщества, состоит в том, что (если они не хотят что-то столь сложное, как nuget), они кодируют библиотеки плагинов так, что он не использует движки представления, такие как razor, вместо этого выводит HTML сам по себе, используя старый примитив способ Response.Write и, таким образом, стать независимым от файлов cshtml. Если вы все еще хотите использовать cshtml - см. эту запись в блоге для предварительной компиляции их в классы.

...