Подтвердить что ты не робот

Представления MEF и Razor внутри библиотеки классов

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

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

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

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

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

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

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

Возможно, мне просто нужно добавить код, чтобы он работал? Любые идеи?

4b9b3361

Ответ 1

Мой предпочтительный способ встраивания Razor Views в библиотеку классов - это скопировать их в папки "Представления/районы" веб-сайта MVC с событием post build. Пользовательские поля просмотра могут быть указаны, если вы переопределите 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 в ваших представлениях. Если вы не копируете свои представления в свой проект веб-сайта, вы можете загружать их из Embedded Resources с помощью пользовательского ViewEngine/VirtualPathProvider. Если это так, вы, безусловно, нуждаетесь в Inherits, поэтому Razor знает, к чему, к сожалению, относится ваш базовый класс.

Удачи.

Ответ 3

Хоссама,

Сообщение, о котором вы говорите, это то, что Дарин уже предложил. Основная нижняя сторона этого подхода - использование пользовательского компилятора MvcRazorClassGenerator для преобразования файлов представления CSHTML в файлы классов. Для этого вам нужно установить каждое представление CSHTML в вашем проекте в "Содержимое" и установить "Пользовательский инструмент" в MvcRazorClassGenerator.

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

Мой другой подход состоял в том, чтобы включить CSHTML файлы как Embeded Resources во внешнюю 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.

Ответ 4

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

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

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