28

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

我关注了很多文章(其中大部分已经过时)。事实上,Stack Overflow 上有一篇相当不错的文章:Controllers and Views inside a Class Library

我还检查了文档,VirtualPathProvider并且我已经能够构建一个自定义的文件,它可以在程序集中找到文件并完美地加载它(或者至少将流传输到它)。为此,我遵循了VirtualPathProvider MSDN 上的文档

VirtualFile 也有一个实现,但 VirtualDirectory 还没有。

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

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

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

也许我只需要添加一些代码即可使其工作?有任何想法吗?

4

4 回答 4

7

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

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

<?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 从嵌入式资源加载它们。如果是这种情况,您肯定需要 Inherits,以便 Razor 知道您的视图基类是什么。

祝你好运。

于 2011-11-23T17:41:36.687 回答
3

您可以查看以下博客文章

于 2011-02-15T18:16:03.657 回答
2

霍萨姆,

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

我不能代表 LordALMMa,但我确实下载了编译器源并试了一下,但它并没有完全按照我希望的方式工作。

我的另一种方法是将 CSHTML 文件作为嵌入资源包含在外部 DLL 中,读入文件的原始内容并将视图作为字符串执行(有关示例,请参见 CodeProject 上的 RazorEngine:http://razorengine.codeplex。 com/ )

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

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

当我开始将原型迁移到 MVC 3 时,我遇到了与 LordALMMa 相同的问题,错误“'...Index.cshtml' 处的视图必须来自 WebViewPage 或 WebViewPage”。我会考虑在我的 CSHTML 视图上放置“@inherits System.Web.Mvc.WebViewPage”,看看这是否能让我更接近让它工作。

因为我有一个使用 MVC 3 Razor 的工作 MVC 2.0 原型,所以我不会在它上面浪费大量时间。如果我们需要利用 4.0 框架,我确信我可以使用 WebForms 引擎将 MVC 2.0 移植到 MVC 3.0。

于 2011-05-25T03:13:48.473 回答
0

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

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

社区通常遵循的另一个解决方案是(如果他们不想要像 nuget 这样复杂的东西)他们对插件 DLL 进行编码,这样,它不使用 razor 之类的视图引擎,而是使用旧的原始响应方式自行输出 HTML .Write 从而独立于 cshtml 文件。如果您仍想使用 cshtml - 请参阅此博客条目以将它们预编译到类中。

于 2011-11-23T17:55:32.620 回答