类库中的MEF和Razor视图

时间:2011-02-15 17:56:40

标签: asp.net-mvc asp.net-mvc-3 razor embedded-resource virtualpathprovider

我有一个使用MEF的复合ASP .NET MVC 3 Razor应用程序。如果我要在应用程序的常规Views文件夹下部署插件作为DLL文件和视图(CSHTML),一切都会好起来的。但是这不是很干净,如果我不将视图作为嵌入式资源放在DLL文件中(以及控制器和模型),它就不会是真正的插件。

我发过很多文章(其中大部分都已过时)。实际上Stack Overflow上有一个非常好的:Controllers and Views inside a Class Library

我还检查了VirtualPathProvider的文档,并且我已经能够构建一个自定义文件,在程序集中找到该文件并完美加载它(或至少获取它的流)。为此,我遵循了VirtualPathProvider documentation on MSDN

还有VirtualFile的实现,但VirtualDirectory尚未实现。

这是问题所在。我正在使用Razor的观点。我知道他们需要Razor的web.config文件中的配置规范来构建它们。但是如果我将它们嵌入到DLL中,那么这个配置就会丢失。

我想知道这是不是我不断收到错误的原因:

  

'〜/ Plugins / CRM.Web.Views.CRM.Index.cshtml'中的视图必须派生   来自WebViewPage或WebViewPage。

也许我只需要添加一些代码来使其工作?有什么想法吗?

4 个答案:

答案 0 :(得分:7)

在类库中嵌入Razor Views的首选方法是使用post build事件将它们复制到MVC网站的Views / Areas文件夹中。如果覆盖ViewEngine或VirtualPathProvider,则可以指定自定义视图位置。

对我来说,棘手的部分是让intellisense在这些View类库中工作。首先,您必须将Web.Config添加到View程序集。请注意,您不必将其实际包含在程序集中。它只需要在程序集根目录(或视图文件夹)中。这是一个例子。关注重要的Assemblies / Compilation部分。

<?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 Web项目类型之间发现的主要区别,它们可能导致智能感知错误。

如果仍然收到必须继承的错误,请考虑在视图中使用@Inherits System.Web.Mvc.WebViewPage。如果您没有将视图复制到网站项目中,则可以使用自定义ViewEngine / VirtualPathProvider从嵌入式资源加载它们。如果是这种情况,你肯定需要继承,所以Razor不幸地知道你的视图基类是什么。

祝你好运。

答案 1 :(得分:3)

您可以查看following blog post

答案 2 :(得分:2)

霍山,

你所谈论的帖子是达林已经提出的建议。该方法的主要缺点是使用自定义MvcRazorClassGenerator编译器将CSHTML视图文件转换为类文件。为此,您必须将项目中的每个CSHTML视图设置为Content,并将Custom Tool设置为MvcRazorClassGenerator。

我不能代表LordALMMa说话,但是我确实下载了编译器源代码并给了它一个镜头,但它并不像我希望的那样完全正常工作。

我的另一种方法是将CSHTML文件作为Embeded Resources包含在外部DLL中,读取文件的原始内容并以字符串形式执行视图(请参阅CodeProject上的RazorEngine示例:http://razorengine.codeplex.com/

我不想在企业应用程序中完全依赖RazorEngine,因为我不知道它与所有Razor语法的兼容性如何,所以我暂时放弃了。

我来自我在ASP.NET MVC 2.0中构建的原型,这是一个多租户应用程序。在服务器场上,我们有一个运行应用程序的实例,其中所有客户端共享相同的代码库。在我的MVC 2.0原型中,我能够确定请求的“客户端”,检查覆盖基础的自定义控制器(用于核心代码的自定义),并检查自定义视图(用于自定义视图)核心观点)。这样做是允许我们为每个客户端为每个客户端部署一个“插件”。该软件检测客户端是否具有与请求匹配的自定义控制器以及匹配的自定义操作,如果匹配,则使用自定义控制器/操作。

当我开始将我的原型迁移到MVC 3时,我遇到了与LordALMMa相同的问题,错误“The view at'... Index.cshtml'必须从WebViewPage或WebViewPage派生”。我将在我的CSHTML视图中放置“@inherits System.Web.Mvc.WebViewPage”,看看是否能让我更接近它的工作。

由于我使用MVC 3工作的MVC 2.0原型Razor不是首要任务,因此我不会浪费大量时间。我确信如果我们需要利用4.0 Framework,我可以使用WebForms引擎将MVC 2.0移植到MVC 3.0。

答案 3 :(得分:0)

嘿,我怀疑你有充分的理由想要在DLL中查看。但是也要考虑到将一切打包成一个实体是一种不寻常的方式。

如果您正在开发插件,那么现在人们选择以NUGET格式打包,这也解决了您的问题。它有.nupkg结构,这也是将插件作为包和库分发的一种方式。

社区通常遵循的另一个解决方案是(如果他们不想要像nuget那样精心设计的东西)他们编写插件DLL,使得它不使用像razor这样的视图引擎,而是使用旧的原语单独输出HTML Response.Write的方式因而独立于cshtml文件。如果您仍想使用cshtml - 请参阅this blog entry以预先将其编译为类。