ASP.NET MVC 中单独程序集中的视图
Posted
技术标签:
【中文标题】ASP.NET MVC 中单独程序集中的视图【英文标题】:Views in separate assemblies in ASP.NET MVC 【发布时间】:2010-09-06 09:25:12 【问题描述】:我正在尝试创建一个 Web 应用程序,我希望能够在其中插入单独的程序集。我正在使用 MVC 预览 4 与 Unity 结合进行依赖注入,我用它来从我的插件程序集中创建控制器。我使用 WebForms(默认 aspx)作为我的视图引擎。
如果我想使用视图,我会卡在核心项目中定义的视图上,因为 ASPX 部分是动态编译的。我正在寻找一种将 ASPX 文件包含在不同程序集中的正确方法,而无需完成整个部署步骤。我错过了一些明显的东西吗?还是我应该求助于以编程方式创建我的视图?
更新:我更改了接受的答案。尽管 Dale 的回答非常彻底,但我还是使用了不同的虚拟路径提供程序来寻求解决方案。它就像一个魅力,我认为总共只需要大约 20 行代码。
【问题讨论】:
使用虚拟路径提供程序是否仍然适用于 mvc.net 中的路由? @jmcd:看起来确实如此。 您是否可以发布任何示例代码来完成此操作? 我尝试过的项目被放弃了,所以我没有准备好示例代码。但是,火花视图引擎也允许这样做 (sparkviewengine.com)。我认为那里有一个名为模块的示例,它将不同程序集中的区域分开。 【参考方案1】:我花了很长时间才能从各种部分示例中正常工作,所以这里是从共享库中的 Views 文件夹获取视图所需的完整代码,该共享库的结构与常规 Views 文件夹相同,但所有内容都设置为构建为嵌入式资源。如果通常的文件不存在,它只会使用嵌入的文件。
Application_Start的第一行:
HostingEnvironment.RegisterVirtualPathProvider(new EmbeddedViewPathProvider());
VirtualPathProvider
public class EmbeddedVirtualFile : VirtualFile
public EmbeddedVirtualFile(string virtualPath)
: base(virtualPath)
internal static string GetResourceName(string virtualPath)
if (!virtualPath.Contains("/Views/"))
return null;
var resourcename = virtualPath
.Substring(virtualPath.IndexOf("Views/"))
.Replace("Views/", "OrangeGuava.Common.Views.")
.Replace("/", ".");
return resourcename;
public override Stream Open()
Assembly assembly = Assembly.GetExecutingAssembly();
var resourcename = GetResourceName(this.VirtualPath);
return assembly.GetManifestResourceStream(resourcename);
public class EmbeddedViewPathProvider : VirtualPathProvider
private bool ResourceFileExists(string virtualPath)
Assembly assembly = Assembly.GetExecutingAssembly();
var resourcename = EmbeddedVirtualFile.GetResourceName(virtualPath);
var result = resourcename != null && assembly.GetManifestResourceNames().Contains(resourcename);
return result;
public override bool FileExists(string virtualPath)
return base.FileExists(virtualPath) || ResourceFileExists(virtualPath);
public override VirtualFile GetFile(string virtualPath)
if (!base.FileExists(virtualPath))
return new EmbeddedVirtualFile(virtualPath);
else
return base.GetFile(virtualPath);
让它工作的最后一步是,根 Web.Config 必须包含正确的设置来解析强类型 MVC 视图,因为不会使用 views 文件夹中的设置:
<pages
validateRequest="false"
pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<controls>
<add assembly="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
</controls>
</pages>
需要几个额外的步骤才能使其与 Mono 一起使用。首先,您需要实现 GetDirectory,因为 views 文件夹中的所有文件都是在应用启动时而不是根据需要加载的:
public override VirtualDirectory GetDirectory(string virtualDir)
Log.LogInfo("GetDirectory - " + virtualDir);
var b = base.GetDirectory(virtualDir);
return new EmbeddedVirtualDirectory(virtualDir, b);
public class EmbeddedVirtualDirectory : VirtualDirectory
private VirtualDirectory FileDir get; set;
public EmbeddedVirtualDirectory(string virtualPath, VirtualDirectory filedir)
: base(virtualPath)
FileDir = filedir;
public override System.Collections.IEnumerable Children
get return FileDir.Children;
public override System.Collections.IEnumerable Directories
get return FileDir.Directories;
public override System.Collections.IEnumerable Files
get
if (!VirtualPath.Contains("/Views/") || VirtualPath.EndsWith("/Views/"))
return FileDir.Files;
var fl = new List<VirtualFile>();
foreach (VirtualFile f in FileDir.Files)
fl.Add(f);
var resourcename = VirtualPath.Substring(VirtualPath.IndexOf("Views/"))
.Replace("Views/", "OrangeGuava.Common.Views.")
.Replace("/", ".");
Assembly assembly = Assembly.GetExecutingAssembly();
var rfl = assembly.GetManifestResourceNames()
.Where(s => s.StartsWith(resourcename))
.Select(s => VirtualPath + s.Replace(resourcename, ""))
.Select(s => new EmbeddedVirtualFile(s));
fl.AddRange(rfl);
return fl;
最后,强类型视图几乎可以但不是很完美地工作。模型将被视为无类型对象,因此要获得强类型返回,您需要使用类似
的内容开始共享视图<% var Model2 = Model as IEnumerable<AppModel>; %>
【讨论】:
记住将视图的构建操作设置为“嵌入式资源”也很重要。视图的文件夹也应该存在于 Web 项目中。就我而言,我将视图放在程序集项目的文件夹 Views/Shared/Dynamic.cshtml 中,并且必须在引用程序集的 Web 项目中创建 Views/Shared。【参考方案2】:本质上,这与人们在使用 WebForms 并试图将其 UserControl ASCX 文件编译成 DLL 时遇到的问题相同。我发现这个http://www.codeproject.com/KB/aspnet/ASP2UserControlLibrary.aspx 也可能对你有用。
【讨论】:
【参考方案3】:protected void Application_Start()
WebFormViewEngine engine = new WebFormViewEngine();
engine.ViewLocationFormats = new[] "~/bin/Views/1/0.aspx", "~/Views/Shared/0.aspx" ;
engine.PartialViewLocationFormats = engine.ViewLocationFormats;
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(engine);
RegisterRoutes(RouteTable.Routes);
将视图的“复制到输出”属性设置为“始终复制”
【讨论】:
您可以通过像这样获取 RazorViewEngine 来执行相同操作,而不是创建 WebFormviewEngine:RazorViewEngine engine = ViewEngines.Engines.OfType对所有仍在寻找圣杯的人的补充:如果您不太喜欢 webforms 视图引擎,我已经接近找到它了。
我最近试用了 Spark 视图引擎。除了太棒了,即使我受到威胁我也不会回到 web 表单,它还为应用程序的模块化提供了一些非常好的钩子。他们文档中的示例是使用 Windsor 作为 IoC 容器,但如果您想采用另一种方法,我无法想象它会变得更加困难。
【讨论】:
以上是关于ASP.NET MVC 中单独程序集中的视图的主要内容,如果未能解决你的问题,请参考以下文章
ASP.Net MVC 4. 在单独的程序集中路由控制器返回错误 404
具有插件和多租户支持的 ASP.NET MVC 应用程序以及单独的 AppDomains?