ASP.NET MVC 5.2.6将Razor视图嵌入DLL后无法脱离cshtml运行的问题
解决ASP.NET MVC 5.2.6嵌入视图到DLL后无法加载的问题
兄弟,你这情况我太熟了!你已经成功把视图编译进DLL没错,但默认的Razor视图引擎还在傻乎乎找物理文件呢——它根本不知道要去DLL里掏嵌入的视图资源。得给它换个「导航系统」,也就是自定义视图引擎,让它学会从DLL里找视图。下面一步步来:
第一步:确认视图真的嵌入DLL了
先打个底,确保你的视图已经被正确嵌入到程序集里:
- 右键你的MVC项目 → 「属性」→ 「生成」→ 检查「嵌入的资源」列表里有没有包含
Views文件夹下的所有.cshtml文件;或者直接选中单个.cshtml文件,在右侧属性面板把「生成操作」改成「嵌入的资源」。 - 编译项目后,用ILSpy这类工具打开生成的DLL,看看里面有没有类似
你的项目命名空间.Views.控制器名.视图名.cshtml的嵌入资源,确认这一步没问题再往下走。
第二步:自定义视图引擎,让它从DLL读视图
默认的RazorViewEngine只会找物理文件,咱们得写个继承它的自定义引擎,重写查找视图的逻辑,优先从嵌入资源加载。
新建一个类,比如EmbeddedRazorViewEngine,代码如下:
using System; using System.IO; using System.Reflection; using System.Web.Mvc; using System.Web.Razor; using System.Web.Razor.Generator; using System.Web.Razor.Parser; using Microsoft.CSharp; using System.CodeDom.Compiler; public class EmbeddedRazorViewEngine : RazorViewEngine { private readonly Assembly _currentAssembly; private readonly string _resourceRootNamespace; public EmbeddedRazorViewEngine() { _currentAssembly = Assembly.GetExecutingAssembly(); // 这里替换成你的项目命名空间,比如你的项目叫MyShopMvc,就写"MyShopMvc.Views" _resourceRootNamespace = "你的项目命名空间.Views"; } // 重写查找普通视图的方法 protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) { var resourceName = ConvertViewPathToResourceName(viewPath); if (_currentAssembly.GetManifestResourceInfo(resourceName) != null) { return new EmbeddedRazorView(controllerContext, resourceName, masterPath, ViewPageActivator); } // 找不到嵌入资源就回退到默认逻辑(方便调试) return base.CreateView(controllerContext, viewPath, masterPath); } // 重写查找部分视图的方法 protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) { var resourceName = ConvertViewPathToResourceName(partialPath); if (_currentAssembly.GetManifestResourceInfo(resourceName) != null) { return new EmbeddedRazorView(controllerContext, resourceName, null, ViewPageActivator); } return base.CreatePartialView(controllerContext, partialPath); } // 把视图路径转换成嵌入资源的名称(比如~/Views/Home/Index.cshtml → 命名空间.Views.Home.Index.cshtml) private string ConvertViewPathToResourceName(string viewPath) { var cleanedPath = viewPath.TrimStart('~', '/').Replace('/', '.'); return $"{_resourceRootNamespace}.{cleanedPath}"; } } // 自定义View类,负责读取嵌入资源并渲染 public class EmbeddedRazorView : RazorView { public EmbeddedRazorView(ControllerContext controllerContext, string viewPath, string layoutPath, IViewPageActivator viewPageActivator) : base(controllerContext, viewPath, layoutPath, false, null, viewPageActivator) { } protected override void RenderView(ViewContext viewContext, TextWriter writer, object instance) { using (var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(ViewPath)) { if (resourceStream == null) { // 找不到嵌入资源就用默认渲染逻辑 base.RenderView(viewContext, writer, instance); return; } // 读取嵌入的视图内容 using (var streamReader = new StreamReader(resourceStream)) { var viewContent = streamReader.ReadToEnd(); // 初始化Razor编译相关的对象 var razorLanguage = new CSharpRazorCodeLanguage(); var razorEngine = new RazorTemplateEngine(razorLanguage); var razorHost = new RazorEngineHost(razorEngine) { DefaultNamespace = "你的项目命名空间.Views", DefaultBaseClass = typeof(WebViewPage).FullName }; // 解析并编译视图内容 var parser = razorEngine.CreateParser(); var parseResult = parser.Parse(viewContent); var codeGenerator = razorEngine.CreateCodeGenerator(ViewPath, razorHost); codeGenerator.GenerateCode(parseResult); var compileParams = new CompilerParameters { GenerateInMemory = true, ReferencedAssemblies = { typeof(Controller).Assembly.Location, typeof(WebViewPage).Assembly.Location, typeof(System.Web.Mvc.MvcHtmlString).Assembly.Location, Assembly.GetExecutingAssembly().Location } }; var codeProvider = new CSharpCodeProvider(); var compileResult = codeProvider.CompileAssemblyFromDom(compileParams, codeGenerator.GeneratedCode); if (compileResult.Errors.HasErrors) { var errorMsg = string.Join("\n", compileResult.Errors.Cast<CompilerError>().Select(e => e.ErrorText)); throw new InvalidOperationException($"视图编译失败:{errorMsg}"); } // 实例化视图并渲染 var viewType = compileResult.CompiledAssembly.GetType(razorHost.DefaultNamespace + "." + Path.GetFileNameWithoutExtension(ViewPath)); var viewInstance = ViewPageActivator.Create(viewContext, viewType) as WebViewPage; if (viewInstance != null) { viewInstance.ViewContext = viewContext; viewInstance.ViewData = viewContext.ViewData; viewInstance.TempData = viewContext.TempData; viewInstance.Execute(); writer.Write(viewInstance.Output); } } } } }
第三步:注册自定义视图引擎
打开Global.asax.cs,在Application_Start方法里,替换掉默认的视图引擎:
protected void Application_Start() { // 其他初始化代码... // 清空默认视图引擎,添加自定义的 ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(new EmbeddedRazorViewEngine()); }
几个关键注意事项
- 一定要把**所有用到的视图(包括布局页、部分视图)**的「生成操作」设为「嵌入的资源」,不然DLL里没有这些资源,还是会报错。
- 代码里的
你的项目命名空间要替换成你实际的项目命名空间,不然找不到对应的嵌入资源。 - 如果调试的时候想保留物理文件,可以把自定义引擎的回退逻辑留着,这样找不到嵌入资源时还能读物理文件;正式发布前再删掉物理文件就行。
做完这些步骤,重新编译项目,删除Views文件夹,再运行网站,应该就能正常加载DLL里的视图了!
内容的提问来源于stack exchange,提问作者Hakan Fıstık
相关产品推荐
相关产品推荐

