类库内的 MEF 和 Razor 视图

发布于 2024-10-17 12:46:18 字数 954 浏览 6 评论 0原文

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

我关注了很多文章(其中大多数已经过时)。事实上,Stack Overflow 上有一个非常好的内容:Controllers and Views inside a Class Library

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

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

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

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

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

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

I have a composite ASP .NET MVC 3 Razor application using MEF. Everything goes fine if I am to deploy plugins as DLL files and views (CSHTML) under the regular Views folder from the application. But this is not very clean and it won't be a real plugin if I don't place views as embedded resources within the DLL files (along with both controllers and models).

I've followed many articles (most of them are outdated). In fact there is one quite good one here on Stack Overflow: Controllers and Views inside a Class Library

I've also checked docs for VirtualPathProvider and I've been able to build a custom one that finds the file within the assembly and loads it perfectly (or at least gets the stream to it). For this I've followed the VirtualPathProvider documentation on MSDN.

There is also an implementation for VirtualFile but not yet for VirtualDirectory.

Here is the problem. I'm working with Razor views. I do know that they need config specs from the web.config file for Razor to build them. But if I embed them within the DLL this config is simply lost.

I wonder if that's why I keep getting the error:

The view at '~/Plugins/CRM.Web.Views.CRM.Index.cshtml' must derive
from WebViewPage, or WebViewPage.

Maybe I just need to add some code to make it work? Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

听,心雨的声音 2024-10-24 12:46:18

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

对我来说,棘手的部分是让智能感知在这些视图类库中工作。首先,您必须将 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 Web 项目类型之间的主要区别,可能会导致智能感知错误。

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

祝你好运。

My preferred way to embed Razor Views in a Class Library is to copy them into the MVC website's Views/Areas folders with a post build event. Custom view locations can be specified if you override the ViewEngine or VirtualPathProvider.

The tricky part for me was getting intellisense to work in these View Class libraries. First, you must add a Web.Config to your View assembly. Note that you don't have to actually include it in your assembly. It only has to be in the assembly root directory (or views folder). Here is an example. Regard the important Assemblies/Compilation section.

<?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>

Next, you need to modify your class library's vbproj file so that all OutputPath elements point to 'bin\' instead of 'Debug\bin\' or 'Release\bin\'. This is the main difference I found between class libraries and ASP.Net web project types that can cause intellisense bugs.

If you still recieve your must inherits error, consider using @Inherits System.Web.Mvc.WebViewPage in your views. If you are not copying your views into your website project, you may be loading them from Embedded Resources using a custom ViewEngine / VirtualPathProvider. If that is the case, you definately need the Inherits so Razor knows what your view base class is unfortunately.

Good luck.

无声无音无过去 2024-10-24 12:46:18

您可以查看 以下博客文章

You might take a look at the following blog post.

想念有你 2024-10-24 12:46:18

Hossam,

你所说的帖子是达林已经建议的。该方法的主要缺点是使用自定义 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”。我将考虑将“@inherits System.Web.Mvc.WebViewPage”放在我的 CSHTML 视图上,看看这是否能让我更接近让它工作。

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

Hossam,

The post you're talking about is what Darin has already suggested. The main down side to that approach is using the custom MvcRazorClassGenerator compiler to convert the CSHTML view files in to class files. To do so you have to set every CSHTML view in your project to Content and set the Custom Tool to MvcRazorClassGenerator.

I can't speak for LordALMMa but I did download the compiler source and gave it a shot and it doesn't exactly work the way I was hoping.

My other approach was to include the CSHTML files as Embeded Resources in the external DLL, read in the raw contents of the file and execute the view as a string (See the RazorEngine on CodeProject for an example: http://razorengine.codeplex.com/)

I didn't want to fully depend on the RazorEngine in an enterprise application because I don't know how well it is compatiable with all of the Razor syntax so I gave up on that for now.

I'm coming from a prototype I built in ASP.NET MVC 2.0 that is a multi-tennant application. On a server farm we have one instance of an application running where all clients share the same code base. In my MVC 2.0 prototype I was able to determine what "client" the request was being made for, check for a custom controller that over-rides the base (for customizations of the core code) and also check for custom views (for customizations of the core view). What this does is allow us to deploy a "plugin" per say for each client. The software detects if the client has a custom controller that matches the request as well as a custom action that matches and if it does, it uses the customized controller/action instead.

When I started migrating my prototype to MVC 3 I ran in to the same problem as LordALMMa, the error "The view at '...Index.cshtml' must derive from WebViewPage, or WebViewPage". I'll look in to placing "@inherits System.Web.Mvc.WebViewPage" on my CSHTML views and see if that gets me any closer to getting it to work.

Since I have a working MVC 2.0 prototype using MVC 3 Razor is not a top priority and I don't waste a ton of time on it. I'm sure I can port the MVC 2.0 to MVC 3.0 using the WebForms engine if we need to leverage the 4.0 Framework.

萌︼了一个春 2024-10-24 12:46:18

嘿,我怀疑您有充分的理由想要 DLL 内部的视图。然而,还要考虑到这是一种将所有内容打包到一个实体中的不寻常方式。

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

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

Hey I suspect you have good reasons for wanting views inside DLLs. However also consider that it is an unusual way of packaging everything into one entity.

If you are developing a plugin, these days people opt for packaging in the NUGET format, which also solves your kind of problem among other things. It has a .nupkg structure which is also one way of distributing plugins as packages and libraries.

Another solution which communities generally follow is (if they do not want something as elaborate as nuget) they code up the plugin DLLs such that, it does not use view engines like razor, instead outputs HTML all by itself using the old primitive way of Response.Write and thus become independent of cshtml files. If you still want to use cshtml - see this blog entry for precompiling those into classes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文