.Net 4726.0 Razor编译时的小差异

news2024/11/18 20:28:27

前言

几个月前在进行着.Net 472到6.0的升级,复用原有代码,在对Razor进行迁移中,发现原运行正常的代码,却存在报错,深入研究发现是Core下对Razor编译有一些变动。

问题复现

472 创建视图

新建.Net Framework下Mvc,增加一个简单视图如下。

@{
    Layout = null;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - RazorDemo472.Web</title>
    @{
        var headContent = "headContent";
    }
</head>
<body>
    <h1>@headContent</h1>
</body>
</html>

暂不论这个变量定义的位置合不合适,或许应该全局定义,但是总归在472下是正常的,可运行的。
489011e3d3750c005d83e8a28d7e3df2.png

6.0 创建视图

新建Asp.Net Core Mvc项目,同样使用如上简单视图

@{
    Layout = null;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - RazorDemo6.Web</title>
    @{
        var headContent = "headContent";
    }
</head>
<body>
    <h1>@headContent</h1>
</body>
</html>

如果只是这样,不做任何设置,编译时候就会报错。
2f73b506c28f8705c851a0c83b40d16e.png

6.0 启用运行时编译

因为整体迁移视图太多,很多视图没有如上的写法(head中的变量在body中使用),并且在472下运行正常,也没有改动Razor视图上的代码。为了加快迁移后启动的速度,不至于编译太长时间,另外也想着视图内容能够实时更新不用关了再重开,我们把构建和发布时编译Razor给关闭了。

9eb45c0ba33f870b5418e0376f2c34c5.png
此处在Demo中使用Razor运行时编译

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews()
    .AddRazorRuntimeCompilation();

var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.MapDefaultControllerRoute();
app.Run();

启动项目,请求页面后便是在运行时发现页面报错。
4914de768cebf0d041788ce58a356f2e.png

当局部页面(整个迁移过程中只发现两个页面存在如上写法)出现如上错误后,开始找根本问题所在。

问题分析

6.0因关闭了构建和发布编译,使得Razor中出现代码报错无法知道,如开启构建和发布编译,则会提示代码报错,但为何同样的代码,472可用,6.0会报错?

先开启6.0下构建和发布编译,对比下472和6.0的差异在何处。

472 IL分析

将472启动后访问页面,再找到对应视图dll,如下是复现时的dll代码,Razor视图没有分块编译,整个变量时可以在head和body共享的。

c632039166c0bbbf90edb5c558cd5638.png

6.0 IL分析

6.0项目改造一下,开启构建与发布编译,移除Razor的运行时编译,更改一下视图内容,以避免变量构建时直接报错。

@{
    Layout = null;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    @{
        var headContent = "headContent";
    }
    <title>@ViewData["Title"] - RazorDemo6.Web @headContent</title>
</head>
<body>
    @{
        var headContent1 = "headContent1";
    }
    <h1>@headContent1</h1>
</body>
</html>

项目构建,从生成的项目dll中找到代码,Asp.Net Core Razor视图编译时会按照tag分块,head和body分开,以至于变量不能在两个tag间共用。
25904da14701041084deceb0534ff810.png

源码分析

472 源码分析

  1. 首先通过ViewEngine查找视图文件。
    2cd3d4b945d4d058bb1ad7f0655a0684.png

  2. 再进入到对应的RazorViewEngine(继承父类VirtualPathProviderViewEngine)中,存在CreateView方法,返回IView,其实现即RazorView(继承父类BuildManagerCompiledView)。
    f5842422f911870bcc4a1614d5f4aad2.png

  3. 开始准备ViewContext和output, 调用的是RazorView中的Render方法。
    e1acdd6f8440b66fd974c6a949d043ac.png

  4. Rende中调用BuildManager.GetCompliedType(ViewPath),完成将cshtml编译成动态视图类。再执行ViewPageActivator.Create方法将动态生成的视图类写入到文件夹中(注意此处是不存在则生成,存在则使用现有的)。
    fb0e09b49d0246adae71c0a341e8ca2c.png

从其内部实现中也可看到,输出的程序集以App_Web_为前缀。

4120403c8eb464e4c27e353dd1f312fa.png

如下简便描述下BuilderManager内部调用过程。

// BuilderManager.cs 
GetCompiledType()
   GetVirtualPathObjectFactory()
      GetVPathBuildResultWithAssert()
        GetVPathBuildResultWithNoAssert()
          GetVPathBuildResultInternal()
            CompilationLock.GetLock()
            CompileWebFile() //Core
              buildResult = buildProvider.GetBuildResult(results); //build
                 CreateBuildResult():BuildProvider.cs
            CompilationLock.ReleaseLock()

6.0 源码分析

当使用运行时编译时,会调用扩展方法注册到服务容器中。

builder.Services.AddControllersWithViews()
    .AddRazorRuntimeCompilation();

该扩展方法AddRazorRuntimeCompilation中会完成Razor转换所需的一些服务注册。
bd0c3b907a6ac74f48b47314ca9e1c3e.png

在这其中会实例化好RazorProjectEngine(原RazorTemplateEngine更名)。当进行视图编译时,会将文件加载,再由RazorProjectEngine负责将其语法分析,转换,构建成文本,最终生成代码并生成类。

7d8b3deb699cb69eea1f06f9a1a94d6e.png

再有了TagHelper加持后,此处生成的cSharpDocument.GeneratedCode便是会按照TagHelperScope分块。

b7cb5f9c928100fb7ba099beb1cbfc5d.png

而生成这些tagHelper的地方则处于ViewComponentTagHelperTargetExtension.cs中。

c2f01a8df0e10aea61da6db7e1038cb5.png

控制台中使用Razor渲染

当直接使用Razor模板引擎时,并不会按照tag分块(或是说没接入tag功能),简单使用一个例子来做对比。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    @{
        var headContent = "headContent";
    }
    <title>RazorDemo6.Web @headContent</title>
</head>
<body>
    @{
        var headContent1 = "headContent1";
    }
    <h1>@headContent1</h1>
</body>
</html>

实例化RazorProjectEngine,加载文件,直接输出生成的代码。

using Microsoft.AspNetCore.Razor.Language;

var project = RazorProjectFileSystem.Create(Directory.GetCurrentDirectory());
var engine = RazorProjectEngine.Create(RazorConfiguration.Default, project, null);
var file = project.GetItem("Index.cshtml");
var codeDocument = engine.Process(file);
var code = codeDocument.GetCSharpDocument().GeneratedCode;
Console.WriteLine(code);
Console.ReadLine();

没有了TagHelper后,生成的类中不会将head和body分块。而是只按照c#代码和html分开。

// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(Razor.Template), @"default", @"/Index.cshtml")]
namespace Razor
{
    #line hidden
    [global::Microsoft.AspNetCore.Razor.Hosting.RazorSourceChecksumAttribute(@"SHA1", @"755e7386ecf54079dc3cddaaeb531c72eabc1d9a", @"/Index.cshtml")]
    public class Template
    {
        #pragma warning disable 1998
        public async override global::System.Threading.Tasks.Task ExecuteAsync()
        {
            WriteLiteral("<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n    <meta charset=\"utf-8\" />\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\r\n");
#nullable restore
#line 6 "C:\Projects\RazorProjectEngine\RazorLanguage.Terminal\bin\Debug\net7.0\Index.cshtml"
        var headContent = "headContent";
#line default
#line hidden
#nullable disable
            WriteLiteral("    <title>RazorDemo6.Web ");
#nullable restore
#line 9 "C:\Projects\RazorProjectEngine\RazorLanguage.Terminal\bin\Debug\net7.0\Index.cshtml"
                     Write(headContent);
#line default
#line hidden
#nullable disable
            WriteLiteral("</title>\r\n</head>\r\n<body>\r\n");
#nullable restore
#line 12 "C:\Projects\RazorProjectEngine\RazorLanguage.Terminal\bin\Debug\net7.0\Index.cshtml"
        var headContent1 = "headContent1";
#line default
#line hidden
#nullable disable
            WriteLiteral("    <h1>");
#nullable restore
#line 15 "C:\Projects\RazorProjectEngine\RazorLanguage.Terminal\bin\Debug\net7.0\Index.cshtml"
   Write(headContent1);
#line default
#line hidden
#nullable disable
            WriteLiteral("</h1>\r\n</body>\r\n</html>");
        }
        #pragma warning restore 1998
    }
}
#pragma warning restore 1591

参考

  1. https://www.cnblogs.com/artech/archive/2012/09/04/razor-view-engine-01.html

  2. https://www.cnblogs.com/artech/archive/2012/09/05/razor-view-engine-02.html

  3. https://juejin.cn/post/7130956013242417166

  4. https://github.com/dotnet/aspnetcore/tree/cd9340856ed85215a911c97c44d52373f6cba2f9/src/Razor/Microsoft.AspNetCore.Razor.Language/src

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/705244.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

常州工学院单片机及应用系统设计2021-2022 学年第 二 学期 考试类型 开卷 课程编码 0302005

第一题 #include "SC95F861x_C.H" #include <INTRINS.H> unsigned char keydata0; void delay(unsigned int timer) //延时函数 { while(timer>0) timer--; } void IOinit() { P5CON0x00; P5PH0x03; P3CON0xFF; P3PH0xFF; } void readke…

字节跳动算法 提前批offer复盘

作者 | zjwang 面试锦囊之面经分享系列&#xff0c;持续更新中 欢迎后台回复"面试"加入讨论组交流噢 写在前面 北航本硕&#xff0c;非科班对搜索推荐比较感兴趣&#xff0c;平时看的文章比较多&#xff0c;所以聊的比较偏这一块大四时一段五个月的nlp方向实习&…

Dlib —— Windows下Vs2017编译dlib源码

Dlib Dlib 是一个现代C工具包&#xff0c;包含机器学习算法和 用于创建复杂软件的工具&#xff0c;C解决现实世界的问题。 它用于工业界和学术界的广泛领域 包括机器人、嵌入式设备、手机和大型高 性能计算环境。Dlib的开源许可允许您在任何应用程序中免费使用它。   Dlib相关…

项目计划、进度与控制

思维导图 项目计划、进度与控制-思维导图 第一部分 项目管理概述 什么是项目 约瑟夫朱兰博士也说过&#xff0c;项目就是在已经确定好的时间内必须解决的问题 什么是项目管理 图1-1 项目管理就是工具、人和系统 image.png 组织是人的集合&#xff0c;过程是人在处理。如果人…

Docker cuda libnvidia-compute Invalid cross-device link

docker apt 安装 cuda rootyeqiang-PC:/opt/speccpu2006# docker exec -it 881 /bin/bash root8815d0425401:/# apt --fix-broken install Reading package lists... Done Building dependency tree Reading state information... Done Correcting dependencies... Don…

Redis 事务与数据持久化

目录 一、Redis 事务 1.1 事务本质 1.2 数据库事务与redis事务 1.2.1 数据库事务 1.2.2 Redis事务 1.2.2.1 两种错误不同处理方式 1&#xff09;代码语法错误&#xff08;编译时异常&#xff09; 2&#xff09;代码逻辑错误&#xff08;运行时错误&#xff09; 1.2.2.2 这种做…

线性代数行列式的几何含义

行列式可以看做是一系列列向量的排列&#xff0c;并且每个列向量的分量可以理解为其对应标准正交基下的坐标。 行列式有非常直观的几何意义&#xff0c;例如&#xff1a; 二维行列式按列向量排列依次是 a \mathbf{a} a和 b \mathbf{b} b&#xff0c;可以表示 a \mathbf{a} a和…

目标检测mAP

概述 AP (Average precision) is a popular metric in measuring the accuracy of object detectors like Faster R-CNN, SSD, etc. Average precision computes the average precision value for recall value over 0 to 1. It sounds complicated but actually pretty simple…

安卓热修系列-插件资源冲突解决方案

作者&#xff1a;37手游移动客户端团队 背景 在做插件化过程中&#xff0c;宿主需要用到插件的资源&#xff0c;涉及到加载插件的资源&#xff1b; 因为插件是以apk的方式存在的&#xff0c;所以插件的ID和宿主的ID可能导致重复&#xff1b; 为了解决这个问题&#xff0c;需…

[游戏开发][Unity]点击Play按钮卡顿特别久

一般小工程不会遇到这个问题&#xff0c;我在公司接手了几个老项目&#xff0c;都遇到了这个问题。每次Play卡顿几分钟甚至十几分钟&#xff0c;很是头疼。

DL学习11-nin-mnist

对于使用卷积神经网络加全连接层的结构而言&#xff0c;对于全连接的参数的巨大了&#xff0c;对于简单的任务容易造成过拟合&#xff0c;且会增加模型的额外开销&#xff0c;例如alexnet&#xff0c;vgg等&#xff0c;全连接层的开销会随着参数的增加而爆炸式增长。 nin旨在使…

ELK增量同步数据【MySql->ES】

一、前置条件 1. linux&#xff0c;已经搭建好的logstasheskibana【系列版本7.0X】&#xff0c;es 的plugs中安装ik分词器 ES版本&#xff1a; Logstash版本&#xff1a; &#xff08;以上部署&#xff0c;都是运维同事搞的&#xff0c;我不会部署&#xff0c;同事给力&#…

动态SLAM论文(3) — Detect-SLAM: Making Object Detection and SLAM Mutually Beneficial

目录 1 Introduction 2 Related Work 3 Detect-SLAM 3.1 移动物体去除 3.2 Mapping Objects 3.3 增强SLAM检测器 4 实验 4.1 动态环境下的鲁棒SLAM 4.2. 提升检测性能 5 结论 Abstract&#xff1a;近年来&#xff0c;在SLAM和目标检测方面取得了显著进展&#xff0c;…

使用python sdk添加删除阿里云pvc路由

1. 前言 由于线路供应商sdwan存在单点问题&#xff0c;需要实现线路高可用解决方案&#xff0c;需要设计自动切换阿里云vpc路由解决方案。通过阿里云文档了解&#xff0c;可通过阿里云专有网络Python SDK&#xff0c;通过sdk实现创建、删除、查询等vpc网络相关操作&#xff08…

如何与德科斯米尔Draexlmaier 建立 EDI 连接?

德科斯米尔Draexlmaier&#xff08;以下简称为DRX&#xff09;是一家总部位于德国的汽车零部件供应商和系统集成商&#xff0c;如今已成为全球领先的汽车内部装饰系统、电气和电子系统、电缆技术以及储能系统的制造商之一。EDI 帮助DRX与其交易伙伴之间实现信息流的一致性、无误…

CHATGPT使用笔记

CHATGPT是帮你做事&#xff0c;而不是替你做事 1、联网插件&#xff1a; 使用Webpilot插件联网时还可以同时使用其它两种插件&#xff08;一次可以同时使用三个插件&#xff09;&#xff0c;而使用Web Browsing插件功能联网时无法使用插件功能&#xff08;联网功能和插件只能…

SpringBoot2+Vue2实战(八)文件上传实现

一、文件上传 创建数据库表 Files import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data;Data TableName("sys_file") public cl…

18.RocketMQ中消息重复的场景和幂等处理

highlight: arduino-light 消息重复的场景 发送消息异常,重试发送导致消息重复★ 当一条消息已被成功发送到服务端并完成持久化。此时出现网络闪断或者客户端宕机&#xff0c;导致服务端对生产者的确认应答失败。生产者发送消息到mq时发送成功未获取到响应,然后生产者进行消息发…

信号链噪声分析18

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 提示&#xff1a;这里可以添加技术概要 到目前为止&#xff0c;我们考虑的是基带采样情况&#xff0c;即所有目标信号均位于第一奈奎斯特区内。 图 显示了另外一种情况&#xff0c;其中采样信号频带局限于第一奈奎斯…

5.8.1 TCP概述

5.8.1 TCP概述 TCP是在Internet中TCP/IP协议家族中最为重要的协议之一&#xff0c;因特网中各种网络特性参差不齐&#xff0c;所以必须要有一个功能很强的互联网可靠传输协议的要求&#xff0c;TCP特点要与UDP特点对比来看。 UDP特点TCP特点无连接面向连接不可靠的服务可靠的…