使用 Source Generator 自动生成 WEB API

使用 Source Generator 自动生成 WEB API

Intro

上次我们介绍了使用 Source Generator 的应用,有小伙伴留言说想要自动生成一套 ABP 相关的东西,我对 ABP 不怎么熟悉,所以写了一个简单版的雏形,可以根据自定义的模板去动态生成,有需要的可以参考一下

Generator 示例

[Generator]
public class ControllersGenerator : ISourceGenerator
{public void Initialize(GeneratorInitializationContext context){}public void Execute(GeneratorExecutionContext context){var appDbContextType = context.Compilation.GetTypeByMetadataName("AspNetCoreWebDemo.Models.AppDbContext");// 从编译信息中获取 DbSet<> 类型var dbContextType = context.Compilation.GetTypeByMetadataName(typeof(DbSet<>).FullName);// 获取 DbContext 中的 DbSet<> 属性var propertySymbols = appDbContextType.GetMembers().OfType<IMethodSymbol>().Where(x => x.MethodKind == MethodKind.PropertyGet&& x.ReturnType is INamedTypeSymbol{IsGenericType: true,IsUnboundGenericType: false,} typeSymbol&& ReferenceEquals(typeSymbol.ConstructedFrom.ContainingAssembly, dbContextType.ContainingAssembly)).ToArray();var propertyReturnType = propertySymbols.Select(r =>Tuple.Create((INamedTypeSymbol)r.ReturnType, r.Name.Replace("get_", "", StringComparison.OrdinalIgnoreCase))).ToArray();(string typeName, string propertyName)[] models = propertyReturnType.Select(t => (t.Item1.TypeArguments.First().Name, t.Item2)).ToArray();//Debugger.Launch();foreach (var additionalFile in context.AdditionalFiles){var templateName = Path.GetFileNameWithoutExtension(additionalFile.Path).Replace("Template", "");var template = additionalFile.GetText(context.CancellationToken);if (template is not null){foreach (var (typeName, propertyName) in models){var code = template.ToString().Replace("{PropertyName}", propertyName, StringComparison.OrdinalIgnoreCase).Replace("{TypeName}", typeName, StringComparison.OrdinalIgnoreCase);context.AddSource($"{propertyName}{templateName}", code);}}}}
}

上面的示例通过 AdditionalFiles 来获取要生成的代码模板,替换模板内的部分 Placeholder,最终生成期望的代码,而且模板可以是多个模板,也就会生成 N 多份内容

Template 模板

Service 模板

Service 模板和上一篇文章中生成的代码基本是一样的,只是命令空间不同

上一篇文章中是在代码里写死的代码,而现在我们则是把相同的部分抽出来独立成一个模板,利用模板来动态生成最终的代码,这样的好处在于,我们最终可以在项目里配置而不是直接写死在 Generator 代码里,会更加的灵活,修改起来也比较方便

using AspNetCoreWebDemo.Models;
using WeihanLi.EntityFramework;namespace AspNetCoreWebDemo.Business
{public partial interface I{PropertyName}Service: IEFRepository<AppDbContext, {TypeName}>{}public partial class {PropertyName}Service: EFRepository<AppDbContext, {TypeName}>,  I{PropertyName}Service{public {PropertyName}Service(AppDbContext dbContext) : base(dbContext){}}
}

Controller 模板

Controller 模板就是我们要来生成 API 的模板,实现了比较简单的增删改查,而且声明的类型是分部类(partial) 这样我们就可以比较方便进行扩展,在同一个类增加自定义的逻辑

using AspNetCoreWebDemo.Business;
using AspNetCoreWebDemo.Models;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using WeihanLi.Common.Helpers;
using WeihanLi.Common.Models;
using WeihanLi.EntityFramework;namespace AspNetCoreWebDemo.Controllers
{[Route("api/[controller]")]public partial class {PropertyName}Controller : ControllerBase{private readonly I{PropertyName}Service _service;public {PropertyName}Controller(I{PropertyName}Service service){_service = service;}[HttpGet]public Task<IPagedListResult<{TypeName}>> List([FromQuery] PagedRequest request){return _service.PagedAsync(request.PageNum, request.PageSize, ExpressionHelper.True<{TypeName}>(), x => x.Id, false,HttpContext.RequestAborted);}[HttpGet("{id}")]public Task<{TypeName}> Details(int id){return _service.FetchAsync(x => x.Id == id,HttpContext.RequestAborted);}[HttpPost]public async Task<{TypeName}> Create([FromBody] {TypeName} model){var result = await _service.InsertAsync(model);model.Id = result;return model;}[HttpPut("{id}")]public async Task<{TypeName}> Update(int id, [FromBody] {TypeName} model){model.Id = id;var result = await _service.UpdateWithoutAsync(model, x => x.Id);return model;}[HttpDelete("{id}")]public async Task<ActionResult<{TypeName}>> Delete(int id){var t = await _service.FetchAsync(x => x.Id == id);if (t is null){return NotFound();}await _service.DeleteAsync(x => x.Id == id);return t;}}
}

Project Structure

上面是 Generator 的一些介绍,接着我们来看一个使用这个 Genertor 的项目结构

项目里只有一个 DbContext 和 Models,以及我们要自定义的模板

UsersController 这里是为了测试分部类的自定义 controller 补充逻辑,UserController 文件的内容如下:

Generated Files

根据 Controller 模板生成的 controller 文件如下:

using AspNetCoreWebDemo.Business;
using AspNetCoreWebDemo.Models;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using WeihanLi.Common.Helpers;
using WeihanLi.Common.Models;
using WeihanLi.EntityFramework;namespace AspNetCoreWebDemo.Controllers
{[Route("api/[controller]")]public partial class UsersController : ControllerBase{private readonly IUsersService _service;public UsersController(IUsersService service){_service = service;}[HttpGet]public Task<IPagedListResult<User>> List([FromQuery] PagedRequest request){return _service.PagedAsync(request.PageNum, request.PageSize, ExpressionHelper.True<User>(), x => x.Id, false,HttpContext.RequestAborted);}[HttpGet("{id}")]public Task<User> Details(int id){return _service.FetchAsync(x => x.Id == id,HttpContext.RequestAborted);}[HttpPost]public async Task<User> Create([FromBody] User model){var result = await _service.InsertAsync(model);model.Id = result;return model;}[HttpPut("{id}")]public async Task<User> Update(int id, [FromBody] User model){model.Id = id;var result = await _service.UpdateWithoutAsync(model, x => x.Id);return model;}[HttpDelete("{id}")]public async Task<ActionResult<User>> Delete(int id){var t = await _service.FetchAsync(x => x.Id == id);if (t is null){return NotFound();}await _service.DeleteAsync(x => x.Id == id);return t;}}
}

Configure

来看一下 Startup 的配置吧,Startup 中我只配置了 DbContext 和 注册服务配置,详细如下:

public class Startup
{public Startup(IConfiguration configuration){Configuration = configuration;}public IConfiguration Configuration { get; }public void ConfigureServices(IServiceCollection services){services.AddDbContext<AppDbContext>(options =>options.UseInMemoryDatabase("Test"));services.RegisterAssemblyTypesAsImplementedInterfaces(x => x.Name.EndsWith("Service"), ServiceLifetime.Scoped, typeof(Startup).Assembly);services.AddControllers();}public void Configure(IApplicationBuilder app, IWebHostEnvironment env){app.UseRouting();app.UseEndpoints(endpoints =>{endpoints.MapControllers();});// 数据初始化using var scope = app.ApplicationServices.CreateScope();var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();dbContext.Users.Add(new User() { Id = 1, Age = 10, Name = "Xiao Ming", });dbContext.SaveChanges();}
}

Run

dotnet run 运行一下看一下效果吧:

首先访问 /api/users,输出结果如下

访问 /api/users/1

以上两个 endpoint 都是自动生成的,我们再访问 /api/users/test 来测试一下分部类中的接口

可以看到,无论是自动生成的代码还是分部类中自定义的接口都是工作的了

More

如果想要进行更多的自定义,只需要根据需要调整 Generator 获取生成时所需的类型等信息,再调整相应的模板即可,模板通过 AdditionalFiles 来访问,包含进项目中,使用方式如下:

<AdditionalFiles Include="Templates/*.txt" />

模板可以增加多个,可以根据自己的项目需要进行定制,定义好自己的模板,自动生成一套 ABP 相关的代码我觉得也是可以实现的,感兴趣的可以试试看

References

  • https://github.com/WeihanLi/SamplesInPractice/tree/master/SourceGeneratorSample/AspNetCoreWebDemo

  • https://github.com/WeihanLi/SamplesInPractice/blob/master/SourceGeneratorSample/Generators/ControllersGenerator.cs

  • 使用 Source Generator 代替 T4 动态生成代码

  • C# 强大的新特性 Source Generator

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

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

相关文章

Excel中的散点图这么强大,学习了!

全世界只有3.14 % 的人关注了数据与算法之美平时见得最多的也许是柱形图了&#xff0c;但我个人最喜欢的却是散点图。在讲散点图之前&#xff0c;我先阐述一个不太严谨的个人观点。我认为&#xff0c;所有的数据图表都可以分为两类&#xff0c;一类是偏重于展示&#xff0c;一类…

mysql修改字段的顺序_Mysql中如何修改字段的排列顺序?

创建数据表的数据,字段在表中的位置已经确定了。但要修改字段在表中的排列位置,则需要使用ALTER TABLE语句来处理。在MySQL中,修改字段排列位置的基本语法格式如下: ALTER TABLE MODLFY 字段名1 数据类型 FIRST|AFTER 字段名2 在上述格式中,“字段名1”指的是修改位置的字段…

趣读:程序员泪流满面的20个瞬间!

全世界只有3.14 % 的人关注了数据与算法之美【一】老板突然说&#xff0c;想跟你聊一聊你的年终review结果【二】记一次难忘的debug经历【三】——昨晚又加班了吧&#xff1f;——你怎么知道的&#xff1f;【四】老板定下了春节期间on call的人!【五】产品crash了&#xff0c;最…

记一次 .NET 某医院HIS系统 CPU爆高分析

一&#xff1a;背景 1. 讲故事前几天有位朋友加 wx 抱怨他的程序在高峰期总是莫名其妙的cpu爆高&#xff0c;求助如何分析&#xff1f;和这位朋友沟通下来&#xff0c;据说这问题困扰了他们几年????&#xff0c;还请了微软的工程师过来解决&#xff0c;无疾而终&#xff0c…

mysql判断表存在的sql语句_SQL 语句判断已知表是否存在_MySQL

问:怎样用SQL语句来判断已知表是否存在?答:具体解决方法如下:注释:以下代码为通常的引用Dao做的一模块以下为引用的内容&#xff1a;Function fExistTable(strTableName As String) As IntegerDim db As DatabaseDim i As IntegerSet db DBEngine.Workspaces(0).Databases(0)…

全球六大顶级域名动态:7月上旬.COM新增18.4万个

中国IDC评述网07月17日报道&#xff1a;据域名统计机构Whois Source公布的最新数据显示&#xff0c;截至7月15日&#xff0c;全球六大顶级域名&#xff08;.COM、.NET、.ORG、.INFO和.US&#xff09;的域名总量达到了 141,259,651个。其中&#xff0c;.COM域名注册量达104,165,…

如何用大数据找到男/女朋友?

全世界只有3.14 % 的人关注了数据与算法之美小柯25岁&#xff0c;单身男&#xff0c;热衷大数据&#xff0c;并决定认真钻研&#xff0c;用数据分析来实现自己的“脱单计划”。找女友第一步&#xff1a;整理思路找女友第二步&#xff1a;界定问题1、为什么要找女朋友&#xff0…

.NET5 开发手机提词应用,基于内嵌Web服务器及PowerPoint自动化

项目说明我使用电脑录制视频教程的时候&#xff0c;会展示PPT给观众&#xff0c;同时也有一些提示性的文字给我自己看。这就类似于很多电视节目录制现场的“提词器”。节目录制现场的提词器在PC环境下&#xff0c;PowerPoint也具有提词器功能&#xff0c;在编辑PPT的时候&#…

[Linux程序设计][调试][ElectricFence]

gcc –o test test.c –lefence 提前发现动态内存的错误 转载于:https://blog.51cto.com/honglei/934379

薄如冈本,37°恒温发热超薄保暖内衣,既要风度也要温度

问君能有几多愁恰似没穿秋裤遇寒流俗话说的好你在北方的暖气里穿着短袖我在南方的艳阳里瑟瑟发抖为了暖和一点大家都穿得里三层外三层的什么大衣棉袄厚外套都搬出来了但谁都不愿意在冬天穿的像个200斤的胖子好吗&#xff01;&#xff1f;可是为了风度连狗命都不要了吗&#xff…

Blazor Day

关注我们Blazor 是一个 Web UI 框架&#xff0c;Blazor 旨在简化快速的单页面 .Net 浏览器应用的构建过程&#xff0c;它虽然使用了诸如 CSS 和 HTML 之类的 Web 技术&#xff0c;但它使用 C&#xff03;语言和 Razor 语法代替 JavaScript 来构建可组合的 Web UI 。通过提供用于…

学会了Python之后,我的职业生涯突飞猛进

全世界只有3.14 % 的人关注了数据与算法之美身为职场人&#xff0c;收集上万条表格数据做商业分析&#xff0c;裁剪上千张图片&#xff0c;发送数百封邮件...这些都是经常会遇到的场景。我一直期待能有个工具解放我&#xff0c;直到我遇到了Python。Python的魅力很多小伙伴入坑…

Android网络编程

2019独角兽企业重金招聘Python工程师标准>>> Android平台有三种网络接口可以使用&#xff0c;他们分别是&#xff1a;java.net.*(标准Java接口)、Org.apache接口和Android.net.*(Android网络接口)。下面分别介绍这些接口的功能和作用。 1.标准Java接口 java.net.*提…

mac 启动mysql多实例_实践:mysql单机多实例部署(mac)

背景&#xff1a;在自己电脑搭建或测试分布式服务框架时&#xff0c;经常会用多个数据库实例模拟多个环境的情况&#xff0c;因此我把搭建多实例mysql的过程记录下来&#xff0c;方便互相学习和沟通。1.搭建环境1) mac 电脑&#xff0c;版本 10.15.62) mysql版本 8.0.202.搭建…

毫无疑问的是.NET 在信创常用软件适配清单之中

2020年8月份写了一篇文章《.NET Core也是国产化信息系统开发的重要选项》&#xff0c; 这又过去了大半年了&#xff0c;在信创领域发生了很大的变化&#xff0c;今天写这篇文章主要是想从信创常用软件适配清单 看一看.NET 在信创里面的情况。信创常用软件适配清单 是由中国电子…

学习Python编程的19个资源

全世界只有3.14 % 的人关注了数据与算法之美用Python编写代码一点都不难&#xff0c;事实上它一直被赞誉为最容易学的编程语言。如果你准备学习web开发&#xff0c; Python是一个不错的开始&#xff0c;甚至想做游戏的话&#xff0c;用Python来开发游戏的资源也有很多。这是快速…

读《精益商业思维》

五一假期读了程浩的《精益商业思维》&#xff0c;程浩是迅雷的联合创始人之一&#xff0c;现在是职业投资人&#xff0c; 全篇从创业者的角度&#xff0c;也从投资人的角度解析了创业的方法论。书中有大量的互联网公司的案例&#xff0c;都是我们耳熟能详的一些互联网企业&…

mysql中两根竖线什么意思_五线谱中两根竖线是什么意思?

五线谱中的竖线叫做终止线&#xff0c;写在乐曲结束处的右边一条略粗的双纵线。音乐总是由强拍和弱拍交替进行的&#xff0c;这种交替不能杂乱无章、任意安排&#xff0c;而是按照一定的规律构成最小的节拍组织一小节&#xff0c;然后以此为基础循环往复。比如&#xff0c;当两…

三张图读懂机器学习 :基本概念、五大流派与九种常见算法

全世界只有3.14 % 的人关注了数据与算法之美机器学习正在进步&#xff0c;我们似乎正在不断接近我们心中的人工智能目标。语音识别、图像检测、机器翻译、风格迁移等技术已经在我们的实际生活中开始得到了应用&#xff0c;但机器学习的发展仍还在继续&#xff0c;甚至被认为有可…

go 切片取最后一个元素_深挖 Go 之 forrange 排坑指南

今年做个 Dig101 系列&#xff0c;挖一挖技术背后的故事。Dig101: dig more, simplified more and know moregolang 常用的遍历方式&#xff0c;有两种&#xff1a;for 和 for-range。而 for-range 使用中有些坑常会遇到&#xff0c;今天我们一起来捋一捋。文章目录0x01 遍历取…