ASP.NET MVC共享视图报错:找不到SummaryReport视图的解决咨询
我的解决方案包含3个项目:
- Library -> 控制台应用
- Project1 -> MVC项目
- Project2 -> MVC项目
Project1和Project2的视图结构相似,都在Views/Monthly目录下有SummaryReport.cshtml:
- Project1/Project2 \--Views \---Monthly \----SummaryReport.cshtml
两个项目中都有Monthly.cs控制器。我已经把Monthly.cs控制器迁移到Library项目,通过复制为链接的方式实现了控制器在两个MVC项目间的共享。
接下来想复用SummaryReport.cshtml视图:将文件放到Library项目的Views/Monthly目录下,再用同样的复制为链接方式关联到Project1和Project2,但运行时报错:
[InvalidOperationException: The view 'SummaryReport' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/Monthly/SummaryReport.cshtml
~/Views/Monthly/SummaryReport.vbhtml
~/Views/Shared/SummaryReport.cshtml
~/Views/Shared/SummaryReport.vbhtml]System.Web.Mvc.ViewResult.FindView(ControllerContext context) +511
System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) +143
System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +91<br /> System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +795
System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +795<br /> System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList1 filters, ActionResult actionResult) +81
System.Web.Mvc.Async.<>c__DisplayClass21.b__1e(IAsyncResult asyncResult) +188
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +38
System.Web.Mvc.Controller.b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +32
System.Web.Mvc.Async.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +73<br /> System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +52<br /> System.Web.Mvc.Async.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +39
System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +38
System.Web.Mvc.MvcHandler.b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +46
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +73
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +38
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +426
System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step) +172
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +158
我已经给Project1添加了Library项目的引用并配置了依赖关系,请问:
- 报错原因是什么?
- 如何修复?
- 跨项目复用视图的最佳方式是什么?
附MonthlyController中的SummaryReport方法代码:
public ActionResult SummaryReport() { var model = db.Database.SqlQuery<MontlhySummary>("SELECT * FROM monthlySummary").ToList(); ViewBag.Title = "Monthly Report"; return View(model); }
一、报错原因
MVC视图引擎默认仅在当前Web项目的Views目录下查找视图文件,复制为链接只是在项目中创建了文件引用,编译时这些链接文件不会被复制到Web项目的输出目录,运行时视图引擎找不到实际的视图文件,因此抛出异常。另外,你的Library是控制台应用项目,输出类型与Web项目不同,即便视图文件被复制到输出目录,视图引擎也不会去搜索类库的输出路径。
二、修复方法
临时修复(适配复制为链接方式)
- 选中Project1/Project2中通过复制为链接添加的
SummaryReport.cshtml文件,在属性窗口设置:- 复制到输出目录:选择「如果较新则复制」或「始终复制」
- 生成操作:设置为「内容」
- 同时确保Library项目中的视图文件属性也设置为「内容」,且配置复制到输出目录。
这种方式仅能临时解决问题,后续维护容易出现路径或文件同步问题,不推荐长期使用。
可靠修复方案
- 将Library改为类库项目:控制台应用不适合作为MVC控制器和视图的共享载体,改为
类库(.NET Framework)或ASP.NET Web Application(空)项目。 - 自定义视图引擎,添加类库视图搜索路径:
创建自定义视图引擎,让它能定位到Library项目中的视图目录:
在Global.asax的public class SharedViewEngine : RazorViewEngine { public SharedViewEngine() { // 根据实际项目结构调整路径 var sharedViewsPath = @"~/../Library/Views/{1}/{0}.cshtml"; ViewLocationFormats = ViewLocationFormats.Concat(new[] { sharedViewsPath }).ToArray(); PartialViewLocationFormats = PartialViewLocationFormats.Concat(new[] { sharedViewsPath }).ToArray(); } }Application_Start方法中注册:ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(new SharedViewEngine());
三、跨项目复用视图的最佳方式
1. 类库封装+自定义视图引擎
将共享的控制器、视图、模型统一放到ASP.NET MVC类库项目中,保持标准的Views目录结构。Web项目引用该类库后,通过自定义视图引擎指定类库的视图搜索路径,实现视图复用。
2. 嵌入资源方式(推荐长期维护)
将类库中的视图文件设置为嵌入的资源,编写自定义视图引擎从程序集中读取嵌入的视图内容:
public class EmbeddedViewEngine : RazorViewEngine { private readonly Assembly _sharedAssembly; public EmbeddedViewEngine(Assembly sharedAssembly) { _sharedAssembly = sharedAssembly; // 对应嵌入资源的命名空间格式 ViewLocationFormats = new[] { "Library.Views.{1}.{0}.cshtml" }; } protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) { var resourceName = viewPath.Replace(".", "/"); return new EmbeddedResourceView(_sharedAssembly, resourceName); } protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) { var resourceName = partialPath.Replace(".", "/"); return new EmbeddedResourceView(_sharedAssembly, resourceName); } protected override bool FileExists(ControllerContext controllerContext, string virtualPath) { var resourceName = virtualPath.Replace(".", "/"); return _sharedAssembly.GetManifestResourceInfo(resourceName) != null; } } public class EmbeddedResourceView : IView { private readonly Assembly _assembly; private readonly string _resourceName; public EmbeddedResourceView(Assembly assembly, string resourceName) { _assembly = assembly; _resourceName = resourceName; } public void Render(ViewContext viewContext, TextWriter writer) { using (var stream = _assembly.GetManifestResourceStream(_resourceName)) { using (var reader = new StreamReader(stream)) { var html = reader.ReadToEnd(); writer.Write(html); } } } }
在Global.asax注册:
ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(new EmbeddedViewEngine(typeof(MonthlyController).Assembly));
这种方式无需复制文件,视图嵌入在类库中,避免了路径问题,适合长期维护。
3. Razor类库(ASP.NET Core适用)
如果是ASP.NET Core项目,直接使用官方提供的Razor类库项目类型,专门用于封装可复用的Razor视图、页面和控制器,Web项目引用后可直接使用,无需自定义视图引擎,是官方推荐的方案。
内容的提问来源于stack exchange,提问作者Adam Suchi Hafizullah

