.NET之使用Razor生成动态代码

介绍

Razor是一个用于将基于.NET的代码嵌入到网页中的标记语法。Razor语法由 Razor 标记、C#代码 和 HTML 组成。.

目的

借助组件使用 Razor 语法构建动态模板。

实现效果:提前定义一个HTML模板,来动态填充内容并得到结果。

操作

模板准备

<div>
    <table border="1" style="border:1px solid black;text-align:center" cellpadding="1" cellspacing="1">
        <tr>
            <th colspan="2">@Model.UserName</th>
        </tr>
        <tr>
            <td>科目</td>
            <td>成绩</td>
        </tr>
        @foreach (var item in Model.SubjectList)
        {
            <tr>
                <td>@item.SubjectName</td>
                <td>@item.Grade</td>
            </tr>
        }

    </table>
</div>

需要右键设置始终复制

涉及到的UserGradeDto类如下

namespace RazorEngineConsoleApp.Models
{
    public class UserGradeDto
    {
        public string UserName { get; set; } = null!;

        /// <summary>
        /// 科目成绩集合
        /// </summary>
        public IEnumerable<SubjectGradeDto> SubjectList { get; set; } = null!;

        public static UserGradeDto GetInfo()
        {
            return new UserGradeDto
            {
                UserName = "张三",
                SubjectList = new List<SubjectGradeDto>
                    {
                        new SubjectGradeDto
                        {
                            SubjectName = "语文",
                            Grade = 90
                        },
                        new SubjectGradeDto
                        {
                            SubjectName = "数学",
                            Grade = 80
                        },
                        new SubjectGradeDto
                        {
                            SubjectName = "英语",
                            Grade = 70
                        }
                    }
            };
        }
    }

    public class SubjectGradeDto
    {
        /// <summary>
        /// 科目名字
        /// </summary>
        public string SubjectName { get; set; } = null!;

        /// <summary>
        /// 成绩
        /// </summary>
        public int Grade { get; set; }
    }
}

RazorEngine

介绍

RazorEngine 是基于 Microsoft 的 Razor 解析引擎构建的模板引擎,允许您使用 Razor 语法构建动态模板。

组件:

RazorEngine

支持NetFramework4.0和4.5,最新更新时间2017.06.16

仓库地址:https://github.com/Antaris/RazorEngine

RazorEngine.NetCore

支持.NetCore3.1、.NetSandard2.1,最新更新时间2020.06.19

仓库地址:https://github.com/fouadmess/RazorEngine

这两个包都已经不再更新,但是该项目正在寻求新的维护者,感兴趣的可以去研究下源码。

原理

.Net在编译的时候会把.cshtml文件生成一个动态程序集,每次调用,就会每次生成一个不重复的动态程序集,通过设置模板key来实现一次编译,下次会使用缓存,不会生成新的动态程序集。

如果cshtml文件发生了修改,那么还调用缓存的话,那么就不合适了,所以可以将模板key设置为动态的。

  • 根据文件名+修改时间
  • 文件的MD5值作为模板key

Razor模板引擎:https://www.cnblogs.com/green-jcx/p/5786669.html

操作

示例环境:VS2022、.NetCore3.1、控制台程序

引用组件

<ItemGroup>
  <PackageReference Include="RazorEngine.NetCore" Version="3.1.0" />
</ItemGroup>

模板通过强类型方式填充

配置生成的代码

var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "View", "usergrade1.cshtml");
if (!File.Exists(filePath))
{
    Console.WriteLine("模板文件不存在");
    return;
}
//打开并且读取模板
string template = File.ReadAllText(filePath);

//一次编译多次使用
{
    //modelType为null
    //添加模板
    Engine.Razor.AddTemplate("usergrade1", template);
    //编译模板
    Engine.Razor.Compile("usergrade1", modelType: null);
    //运行模板
    string str = Engine.Razor.Run("usergrade1", modelType: null, UserGradeDto.GetInfo());
    Console.WriteLine(str);

    //modelType不为null
    ////添加模板
    //Engine.Razor.AddTemplate("usergrade1", template);
    ////编译模板
    //Engine.Razor.Compile("usergrade1", typeof(UserGradeDto));
    ////运行模板
    //string str = Engine.Razor.Run("usergrade1", typeof(UserGradeDto), UserGradeDto.GetInfo());
    //Console.WriteLine(str);
}

//一次编译一次使用
{
    //var str = Engine.Razor.RunCompile(template, "usergrade1", typeof(UserGradeDto), UserGradeDto.GetInfo());
}

生成结果

<div>
    <table border="1" style="border:1px solid black;text-align:center" cellpadding="1" cellspacing="1">
        <tr>
            <th colspan="2">张三</th>
        </tr>
        <tr>
            <td>科目</td>
            <td>成绩</td>
        </tr>
            <tr>
                <td>语文</td>
                <td>90</td>
            </tr>
            <tr>
                <td>数学</td>
                <td>80</td>
            </tr>
            <tr>
                <td>英语</td>
                <td>70</td>
            </tr>
    </table>
</div>

模板通过dynamic填充

配置生成的代码

var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "View", "usergrade2.cshtml");
if (!File.Exists(filePath))
{
    Console.WriteLine("模板文件不存在");
    return;
}
//打开并且读取模板
string template = File.ReadAllText(filePath);
//dynamic方式
var result = Engine.Razor.RunCompile(template, "templateKey", null, new { UserName = "李思", SubjectList = new List<SubjectGradeDto> { new SubjectGradeDto { SubjectName = "语文", Grade = 90 } } });
Console.WriteLine(result);

这里的SubjectGradeDto内容就是上面UserGradeDto的内容

生成结果和上面示例结果一致,不再张贴。

RazorLight

介绍

在ASP.NET MVC 之外的 .NET Core使用 Razor 从字符串/文件/嵌入式资源构建模板。

官网文档:https://github.com/toddams/RazorLight#quickstart

组件:

RazorLight

支持.NetStandard2.0、.NetCore3.1、.Net5、.Net6,最新更新时间2022.03.19

仓库地址:https://github.com/toddams/RazorLight

操作

示例环境:VS2022、.Net6、控制台程序。(也可以通过依赖注入方式进行操作)

引用组件

<PackageReference Include="RazorLight" Version="2.0.0" />

为了防止后续出现错误:Can't load metadata reference from the entry assembly,可以提前修改csproj文件,增加如下配置

<PropertyGroup>
  <OutputType>Exe</OutputType>
  <TargetFramework>net6.0</TargetFramework>
  <Nullable>enable</Nullable>
  <!--razor配置-->
  <PreserveCompilationContext>true</PreserveCompilationContext>
</PropertyGroup>

具体填充模板操作

public async Task FillTemplateAsync()
{
    //动态编译razor
    var engine = new RazorLightEngineBuilder()
        .UseEmbeddedResourcesProject(typeof(Program))//必须有一个模板的类型
        .SetOperatingAssembly(typeof(Program).Assembly)
        .UseMemoryCachingProvider()
        .DisableEncoding()//禁用编码,否则会把中文字符串编码成Unicode
        .Build();

    var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "View", "usergrade1.cshtml");
    if (!File.Exists(filePath))
    {
        Console.WriteLine("模板文件不存在");
        return;
    }
    //打开并且读取模板
    string template = File.ReadAllText(filePath);

    //运行
    string result = await engine.CompileRenderStringAsync("templateKey", template, UserGradeDto.GetInfo());
    Console.WriteLine(result);
}

输出结果

<div>
    <table border="1" style="border:1px solid black;text-align:center" cellpadding="1" cellspacing="1">
        <tr>
            <th colspan="2">张三</th>
        </tr>
        <tr>
            <td>科目</td>
            <td>成绩</td>
        </tr>
            <tr>
                <td>语文</td>
                <td>90</td>
            </tr>
            <tr>
                <td>数学</td>
                <td>80</td>
            </tr>
            <tr>
                <td>英语</td>
                <td>70</td>
            </tr>
    </table>
</div>

其他的更多的操作可以看官网,我目前就用到这些。

总结

通过该Razor可以实现动态生成代码等操作,可玩性还挺高。

本文示例代码:https://github.com/azrng/dotnet-sample/tree/main/src/RazorSample

资料

Razor语法参考:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/views/razor?view=aspnetcore-6.0