【问题标题】:MEF and Razor Views inside Class Library类库中的 MEF 和 Razor 视图
【发布时间】:2011-06-27 19:21:24
【问题描述】:

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

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

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

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

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

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

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

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

【问题讨论】:

  • 要考虑的事情:如果我要在每个 cshtml 文件上添加“@inherits System.Web.Mvc.WebViewPage”,一切都会很好。但是我不能简单地做到这一点(对于使用常规视图文件已经自动的东西来说,这将是一个巨大的努力)。那么,有什么想法吗?
  • 这篇文章可能会有所帮助,但我现在无法对其进行测试。我会尽快尝试并在此处发布结果/答案。 stackoverflow.com/questions/6465855/…
  • 它不起作用是奇怪的。虚拟路径是所有 asp.net 类使用的文件系统的抽象,因此 Razor 将通过直接访问实际文件系统而不使用虚拟路径提供程序来打破这种模式......很难相信微软人已经做了这么大设计错误。可能是。您的问题在某种程度上是由于相对路径中的错误造成的。看一下这个旧帖子:thecodinghumanist.com/Content/VirtualPathProviderExample.aspx 还有一些软件可以下载...与它相比,您的实现中可能缺少一些东西
  • 链接是旧的,是的,它适用于 aspx 和 ascx。不适用于 CSHTML。
  • 你可以使用我的EmbeddedResourceVirtualPathProvider,它可以通过 Nuget 安装。它从引用的程序集中加载资源,也可以设置为在开发过程中依赖源文件,这样您就可以更新视图而无需重新编译。

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


【解决方案1】:

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

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

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

【讨论】:

  • 嗯,我知道答案不是问题所要寻找的,但我的意思是劝阻提问者不要沿着这种方式解决他更深层次的包装问题。
【解决方案2】:

在类库中嵌入 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 知道您的视图基类是什么。

祝你好运。

【讨论】:

  • 有趣。我会试一试并发布结果。
【解决方案3】:

霍萨姆,

你所说的帖子是达林已经建议的。该方法的主要缺点是使用自定义 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。

【讨论】:

    【解决方案4】:

    你可以看看following blog post

    【讨论】:

    • 有趣。但这需要我预编译所有视图。我不喜欢那个,我只是想在类库中添加 cshtml 文件,仅此而已(嵌入式资源等等)。我认为您根本看不到的另一件事是我正在构建插件!如果我错了,请纠正我,但我从未见过主应用程序引用了一个插件,它甚至根本不知道该插件是否存在! =)
    • @LordALMM,为什么?提供的解决方案如何不满足您在单独程序集中外部化视图的要求?
    • "有趣。但这需要我预编译所有视图。我不喜欢那个,我只是想在类库中添加 cshtml 文件,仅此而已(嵌入式资源等等)。” (2 月 15 日)。有用?是的。但这不是最好的方法。仅仅因为你可以用炸弹杀死一只蚂蚁,但这并不能使它成为完成任务的最佳方法。我正在寻找一种将 Razor 和某种提供程序放在一起的方法,这样我就可以使用嵌入式资源(但不是预编译的类,只是普通的 CSHTML 文件)。
    • 另外,因为如果我要创建可定制的插件,拥有源代码比汇编或 IL 更容易。我正在尝试创建某种 MVC MEF 应用程序,允许我的客户创建 CSHTML 文件并添加他们自己的更多功能(例如带有自定义代码的 SAP)。
    猜你喜欢
    • 2019-02-26
    • 1970-01-01
    • 1970-01-01
    • 2012-12-07
    • 1970-01-01
    • 2018-01-26
    • 1970-01-01
    • 1970-01-01
    • 2013-07-01
    相关资源
    最近更新 更多