news 2026/4/19 23:16:19

.Net如何自定义优雅实现代码生成器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
.Net如何自定义优雅实现代码生成器

需求分析与场景定义

  • 明确代码生成器的目标:生成实体类、API控制器、DTO等常见场景
  • 典型应用场景:快速开发CRUD功能、减少重复编码工作
  • 核心需求:可配置性、模板灵活性、与项目结构无缝集成

具体实现可参考NetCoreKevin的kevin.CodeGenerator模块

基于.NET构建的企业级SaaS智能应用架构,采用前后端分离设计,具备以下核心特性:
前端技术:

  • Vue3前端框架
  • IDS4单点登录系统
  • 一库多租户解决方案
  • 多级缓存机制
  • CAP事件集成
  • SignalR实时通信
  • 领域驱动设计
  • AI智能体框架
  • RabbitMQ消息队列
  • 项目地址:github:https://github.com/junkai-li/NetCoreKevin
    Gitee: https://gitee.com/netkevin-li/NetCoreKevin

第一步:配置模板

模板配置示例如下图所示:

创建kevin.CodeGenerator模块

ICodeGeneratorService接口定义

usingkevin.CodeGenerator.Dto;usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;namespacekevin.CodeGenerator{publicinterfaceICodeGeneratorService{/// <summary>/// 获取区域名称列表/// </summary>/// <returns></returns>Task<List<string>>GetAreaNames();/// <summary>/// 获取区域名称下面的表列表/// </summary>/// <returns></returns>Task<List<EntityItemDto>>GetAreaNameEntityItems(stringareaName);/// <summary>/// 生成代码/// </summary>/// <param name="entityItems"></param>/// <returns></returns>Task<bool>BulidCode(List<EntityItemDto>entityItems);}}

CodeGeneratorService实现

usingkevin.CodeGenerator.Dto;usingMicrosoft.CodeAnalysis;usingMicrosoft.CodeAnalysis.CSharp;usingMicrosoft.CodeAnalysis.CSharp.Syntax;usingMicrosoft.Extensions.Options;usingSystem;usingSystem.Collections.Generic;usingSystem.IO;usingSystem.Linq;usingSystem.Reflection.Metadata;usingSystem.Text;usingstaticMicrosoft.CodeAnalysis.CSharp.SyntaxTokenParser;namespacekevin.CodeGenerator{publicclassCodeGeneratorService:ICodeGeneratorService{privateCodeGeneratorSetting_config;publicCodeGeneratorService(IOptionsMonitor<CodeGeneratorSetting>config){_config=config.CurrentValue;}publicasyncTask<List<string>>GetAreaNames(){return_config.CodeGeneratorItems.Select(t=>t.AreaName).ToList();}publicasyncTask<List<EntityItemDto>>GetAreaNameEntityItems(stringareaName){vararea=_config.CodeGeneratorItems.FirstOrDefault(t=>t.AreaName==areaName);if(area!=default){varentityItems=newList<EntityItemDto>();varpath="..\\..\\"+area.AreaPath.Trim().Replace(".","\\");// 遍历路径下的所有 .cs 文件if(!Directory.Exists(path)){thrownewArgumentException($"CodeGeneratorSetting配置:{areaName}{area.AreaPath}不存在");}else{varcsFiles=Directory.GetFiles(path,"*.cs",SearchOption.AllDirectories);foreach(varfileincsFiles){// 读取文件内容varcode=File.ReadAllText(file);vartree=CSharpSyntaxTree.ParseText(code);varroot=(CompilationUnitSyntax)tree.GetRoot();// 查找所有类声明varclassDeclarations=root.DescendantNodes().OfType<ClassDeclarationSyntax>();foreach(varclassDeclarationinclassDeclarations){// 检查类是否有 Table 特性if(classDeclaration.AttributeLists.Any(list=>list.Attributes.Any(attr=>attr.Name.ToString()=="Table"))){stringdescription="";// 检查类是否有 Description 特性vardescriptionAttr=classDeclaration.AttributeLists.SelectMany(list=>list.Attributes).FirstOrDefault(attr=>attr.Name.ToString()=="Description");if(descriptionAttr!=null){// 获取特性参数值vararg=descriptionAttr.ArgumentList?.Arguments.FirstOrDefault();if(arg?.ExpressionisLiteralExpressionSyntaxliteral){description=literal.Token.ValueText;}}entityItems.Add(newEntityItemDto{AreaName=area.AreaName,EntityName=classDeclaration.Identifier.Text,Description=$"{file}:{description}"});}}}returnentityItems;}}returnnewList<EntityItemDto>();}publicasyncTask<bool>BulidCode(List<EntityItemDto>entityItems){//获取对应的模板文件variRpTemplate=GetBuildCodeTemplate("IRp");varrpTemplate=GetBuildCodeTemplate("Rp");variServiceTemplate=GetBuildCodeTemplate("IService");varservice=GetBuildCodeTemplate("Service");foreach(variteminentityItems){vararea=_config.CodeGeneratorItems.FirstOrDefault(t=>t.AreaName==item.AreaName);if(area!=default){if(item.EntityName.StartsWith("T",StringComparison.OrdinalIgnoreCase)){item.EntityName=item.EntityName.Substring(1);}WriteCode(newDictionary<string,string>{{"%entityName%",item.EntityName},{"%namespacePath%",area.IRpBulidPath}},iRpTemplate,$"../../{area.IRpBulidPath.Trim().Replace(".","\\")}/I{item.EntityName}Rp.cs");WriteCode(newDictionary<string,string>{{"%entityName%",item.EntityName},{"%namespacePath%",area.RpBulidPath}},rpTemplate,$"../../{area.RpBulidPath.Trim().Replace(".","\\")}/{item.EntityName}Rp.cs");WriteCode(newDictionary<string,string>{{"%entityName%",item.EntityName},{"%namespacePath%",area.IServiceBulidPath}},iServiceTemplate,$"../../{area.IServiceBulidPath.Trim().Replace(".","\\")}/I{item.EntityName}Service.cs");WriteCode(newDictionary<string,string>{{"%entityName%",item.EntityName},{"%namespacePath%",area.ServiceBulidPath}},service,$"../../{area.ServiceBulidPath.Trim().Replace(".","\\")}/{item.EntityName}Service.cs");}}returntrue;}/// <summary>/// 获取对应模板文件/// </summary>/// <param name="name"></param>/// <returns></returns>privatestringGetBuildCodeTemplate(stringname){returnFile.ReadAllText("..\\..\\"+"Kevin\\kevin.Module\\kevin.CodeGenerator\\BuildCodeTemplate\\"+name+".txt",encoding:Encoding.UTF8);}/// <summary>/// 生成文件和代码/// </summary>/// <param name="paramters"></param>/// <param name="content"></param>/// <param name="savePath"></param>privatevoidWriteCode(Dictionary<string,string>paramters,stringcontent,stringsavePath){foreach(variteminparamters){content=content.Replace(item.Key,item.Value);}vardir=Path.GetDirectoryName(savePath);if(!Directory.Exists(dir)){Directory.CreateDirectory(dir);}if(File.Exists(savePath)){Console.WriteLine($"文件{savePath}已存在,跳过生成!");}else{File.WriteAllText(savePath,content,Encoding.UTF8);}}}}

CodeGeneratorSettingDto

namespacekevin.CodeGenerator.Dto{publicclassCodeGeneratorSetting{/// <summary>/// 配置文件相关信息/// </summary>publicList<CodeGeneratorItem>CodeGeneratorItems{get;set;}=new();}publicclassCodeGeneratorItem{/// <summary>/// 区域/// </summary>publicstringAreaName{get;set;}="";/// <summary>/// 数据库实体类路径/// </summary>publicstringAreaPath{get;set;}="";/// <summary>/// 仓储接口生成路径/// </summary>publicstringIRpBulidPath{get;set;}="";/// <summary>/// 仓储生成路径/// </summary>publicstringRpBulidPath{get;set;}="";/// <summary>/// 服务接口生成路径/// </summary>publicstringIServiceBulidPath{get;set;}="";/// <summary>/// 服务生成路径/// </summary>publicstringServiceBulidPath{get;set;}="";}}

配置Json文件

////代码生成器配置 .转换成/时要和路径一致 请配置好命名空间和路径对应关系"CodeGeneratorSetting":{"CodeGeneratorItems":[{"AreaName":"App.WebApi.v1",//项目命名"AreaPath":"App.Domain.Entities",//实体类路径"IRpBulidPath":"App.Domain.Interfaces.Repositorie.v1",//仓储接口命名空间和路径"RpBulidPath":"App.RepositorieRps.Repositories.v1",//仓储命名空间和路径"IServiceBulidPath":"App.Domain.Interfaces.Services.v1",//服务接口命名空间和路径"ServiceBulidPath":"App.Application.Services.v1"//服务命名空间和路径}]}

服务注入

services.AddKevinCodeGenerator(options=>{varsettings=Configuration.GetRequiredSection("CodeGeneratorSetting").Get<CodeGeneratorSetting>()!;options.CodeGeneratorItems=settings.CodeGeneratorItems;});

使用

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/19 23:14:57

LumenPnP开源贴片机:从零开始的完整搭建指南

LumenPnP开源贴片机&#xff1a;从零开始的完整搭建指南 【免费下载链接】lumenpnp The LumenPnP is an open source pick and place machine. 项目地址: https://gitcode.com/gh_mirrors/lu/lumenpnp LumenPnP开源贴片机为电子爱好者和创客提供了低成本电子制造的理想解…

作者头像 李华
网站建设 2026/4/19 11:50:02

Windows蓝牙连接PS3控制器的完整解决方案

你是否曾经在Windows电脑上尝试连接PS3控制器&#xff0c;却遭遇了各种连接失败、识别错误的问题&#xff1f;这并非你个人的技术能力问题&#xff0c;而是Windows原生蓝牙栈对PS3外设支持的固有缺陷。现在&#xff0c;通过BthPS3驱动项目&#xff0c;这些困扰将得到彻底解决。…

作者头像 李华
网站建设 2026/4/17 8:27:25

终极Reagent编译器完全指南:从入门到性能优化大师

终极Reagent编译器完全指南&#xff1a;从入门到性能优化大师 【免费下载链接】dify-helm Deploy langgenious/dify, an LLM based app on kubernetes with helm chart 项目地址: https://gitcode.com/gh_mirrors/di/dify-helm 在ClojureScript开发领域&#xff0c;Reag…

作者头像 李华
网站建设 2026/4/17 8:08:35

Audacity音频处理完全攻略:零基础到实战高手

Audacity音频处理完全攻略&#xff1a;零基础到实战高手 【免费下载链接】audacity Audio Editor 项目地址: https://gitcode.com/GitHub_Trending/au/audacity 想要轻松驾驭音频编辑却不知从何入手&#xff1f;Audacity作为业界知名的开源音频处理神器&#xff0c;为初…

作者头像 李华
网站建设 2026/4/17 2:43:23

mp-html LaTeX公式显示终极指南:5个步骤解决小程序公式异常问题

mp-html LaTeX公式显示终极指南&#xff1a;5个步骤解决小程序公式异常问题 【免费下载链接】mp-html mp-html是一个微信小程序HTML组件库&#xff0c;适合用于快速搭建微信小程序界面。特点&#xff1a;组件丰富、易于使用、支持自定义样式。 项目地址: https://gitcode.com…

作者头像 李华
网站建设 2026/4/17 18:33:35

基于BRAM的FPGA验证平台设计:深度剖析

基于BRAM的FPGA验证平台设计&#xff1a;从理论到实战你有没有遇到过这样的场景&#xff1f;你的DUT&#xff08;待测设计&#xff09;跑在200 MHz&#xff0c;逻辑功能看似正常&#xff0c;但某些边缘情况下的输出总是对不上预期。你想抓波形看看&#xff0c;结果ILA&#xff…

作者头像 李华