You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:54:07