.Net Core——用代码写代码?
想要用代码写代码,肯定是绕不开反射的。反射的概念相比都不陌生,只是应用多少就因人而异,今天分享一个代码生成器的思路,仅供参考,不要过分依赖哦。
思路分析
众所周知,利用反射可以在程序运行时获取到任一对象的类型、属性、参数、方法等,并加以调用,利用这些获取到的可以在程序运行时追加各种自定义的功能。以CRUD为例,我们可以利用反射获取到所有的Model并编写代码模板,最终达成用代码生成代码的结果。思路既然能走通,开搞开搞~
编码阶段
首先要确定生成哪些代码。CRUD每天都在做,但大部分都一样,这就导致每天有些时间都在板砖,毫无意义。所以我们需要自动生成全库的CRUD代码。
首先要写一个CRUD的模板代码,考虑到ORM框架太多,这里就以SqlSugar为例:
[HttpGet]
public async Task<IActionResult> GetList(int index = 1, int size = 15)
{
RefAsync<int> count = 0; return PageMsg(await _sqlHelper.DB
.Queryable<Models.AD>()
.OrderBy(x => x.Weight)
.ToPageListAsync(index, size, count), count.Value);
} [HttpPost, Role("添加广告")]
public async Task<IActionResult> Add([FromForm] Models.AD ad)
{
Models.AD add = await _sqlHelper.DB
.Insertable(ad)
.RemoveDataCache()
.ExecuteReturnEntityAsync(); return Ok(add);
} [HttpPut, Role("修改广告")]
public async Task<IActionResult> Cag([FromForm] Models.AD ad)
{
int result = await _sqlHelper.DB
.Updateable(ad)
.RemoveDataCache()
.ExecuteCommandAsync();
return YesOrNo(result > 0);
} [HttpDelete]
public async Task<IActionResult> Delete([FromForm] int id)
{
int result = await _sqlHelper.DB
.Deleteable<Models.AD>()
.Where(x => x.ID == id)
.ExecuteCommandAsync();
return YesOrNo(result > 0);
}
能看出这是一组操作AD类的CRUD,接下来就是需要把上面的模板封装起来,AD这种表的名称作为变量输入即可,如果想控制代码文件的路径,也可以作为参数传入。完整版:
public void Make(string tableName, string path)
{
if (!Directory.Exists(path))
Directory.CreateDirectory(path); using FileStream fs = new FileStream($"{path}/{tableName}Controller.cs", FileMode.Append); System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append("using System.Threading.Tasks;");
sb.Append("\n");
sb.Append("using XXXX.Services.DB;");
sb.Append("\n");
sb.Append("using Microsoft.AspNetCore.Authorization;");
sb.Append("\n");
sb.Append("using Microsoft.AspNetCore.Mvc;");
sb.Append("\n");
sb.Append("using SqlSugar;");
sb.Append("\n");
sb.Append("namespace XXXX.Controllers.v1");
sb.Append("\n");
sb.Append("{");
sb.Append("\n");
sb.Append(" [ApiController, Route(\"v1/[controller]/[action]\"), Authorize]");
sb.Append("\n");
sb.Append($" public class {tableName}Controller : BaseController");
sb.Append("\n");
sb.Append(" {");
sb.Append("\n");
sb.Append(" private readonly SqlHelper _sqlHelper;");
sb.Append("\n");
sb.Append($" public {tableName}Controller(SqlHelper sqlHelper)");
sb.Append("\n");
sb.Append(" {");
sb.Append("\n");
sb.Append(" _sqlHelper = sqlHelper;");
sb.Append("\n");
sb.Append(" }");
sb.Append("\n");
sb.Append(" [HttpGet]");
sb.Append("\n");
sb.Append($" public async Task<IActionResult> GetList(int index = 1, int size = 15)");
sb.Append("\n");
sb.Append(" {");
sb.Append("\n");
sb.Append($" RefAsync<int> count = 0;");
sb.Append("\n");
sb.Append(" return Ok(new{");
sb.Append("\n");
sb.Append($" rows = await _sqlHelper.DB.Queryable<Models.{tableName}>().ToPageListAsync(index, size, count),");
sb.Append("\n");
sb.Append(" total = count.Value");
sb.Append("\n");
sb.Append(" });");
sb.Append("\n");
sb.Append(" }");
sb.Append("\n");
sb.Append(" [HttpPost]");
sb.Append("\n");
sb.Append($" public async Task<IActionResult> Add([FromForm] Models.{tableName} {tableName.ToLower()})");
sb.Append("\n");
sb.Append(" {");
sb.Append("\n");
sb.Append($" Models.{tableName} add = await _sqlHelper.DB.Insertable({tableName.ToLower()}).RemoveDataCache().ExecuteReturnEntityAsync();");
sb.Append("\n");
sb.Append(" return Ok(add);");
sb.Append("\n");
sb.Append(" }");
sb.Append("\n");
sb.Append(" [HttpPut]");
sb.Append("\n");
sb.Append($" public async Task<IActionResult> Cag([FromForm] Models.{tableName} {tableName.ToLower()})");
sb.Append("\n");
sb.Append(" {");
sb.Append("\n");
sb.Append($" int result = await _sqlHelper.DB.Updateable({tableName.ToLower()}).RemoveDataCache().ExecuteCommandAsync();");
sb.Append("\n");
sb.Append(" return YesOrNo(result>0);");
sb.Append("\n");
sb.Append(" }");
sb.Append("\n");
sb.Append(" [HttpDelete]");
sb.Append("\n");
sb.Append($" public async Task<IActionResult> Cag([FromForm] int id)");
sb.Append("\n");
sb.Append(" {");
sb.Append("\n");
sb.Append($" int result = await _sqlHelper.DB.Deleteable<Models.{tableName}>().Where(x => x.ID == id).ExecuteCommandAsync();");
sb.Append("\n");
sb.Append(" return YesOrNo(result>0);");
sb.Append("\n");
sb.Append(" }");
sb.Append("\n");
sb.Append(" }");
sb.Append("\n");
sb.Append("}"); using StreamWriter sw = new StreamWriter(fs); sw.WriteLine(sb.ToString());
}
为了方便理解,刻意美化了一下。
OK,代码模板搞好了,该从哪里拿到所有的Model呢?思路是先加载程序集,然后找到存放Model的命名空间,然后找到命名空间下面所有符合条件的Class,然后就可以拿到具体的名称,从而调用代码模板进行文件生成,像这样:
var assembly = Assembly.Load("程序集名称"); var types = assembly.ExportedTypes.Where(a => a.FullName.Contains("Model所处的命名空间")).ToList();
//指定
string path = $"{Directory.GetCurrentDirectory()}/autoCode"; foreach (var item in types)
{
string tableName = item.FullName.Split('.')[^1]; Make(tableName, path);
}
接下来直接运行,走完以后的结果是这样的:
随便打开一个文件,是这样的:
效果还是不错的。
如果需要更加精细的控制,可以预先在Model设置特性,然后通过判断特性是否存在来决定如何生成代码,举个栗子:
var assembly = Assembly.Load("程序集名称"); var types = assembly.ExportedTypes.Where(a => a.FullName.Contains("Model所处的命名空间")).ToList(); //指定
string path = $"{Directory.GetCurrentDirectory()}/autoCode"; foreach (var item in types)
{
//获取所有公开属性
foreach (var prop in item.GetProperties())
{
MyAttribute attr = method.GetCustomAttribute(typeof(MyAttribute), true) as MyAttribute;
if(attr is null)
{
//进入这里代表该属性没有附加 MyAttribute
}
}
}
当然,还可以获取MyAttribute的属性拿过来进行判断等等。而实际上很多ORM框架也是用类似手段实现Code First、DB First,不过复杂程度比较高。
这样批量生成代码有点像活字印刷,虽然可以解决大量机械重复的工作,但灵活度还是不够高;人脑还是无法替代的,奇技淫巧,见笑了。
.Net Core——用代码写代码?的更多相关文章
- Docker & ASP.NET Core (1):把代码连接到容器
和这种蛋糕一样,Docker的容器和镜像也是使用类似的分层文件系统构建而成的. 这样做的好处就是可以节省硬盘空间,也利于复用等等.因为Docker基于镜像创建容器的时候,其镜像是共享的:而且镜像里面的 ...
- .NET/C# 异常处理:写一个空的 try 块代码,而把重要代码写到 finally 中
不知你是否见过 try { } finally { } 代码中,try 块留空,而只往 finally 中写代码的情况呢?这种写法有其特殊的目的. 本文就来说说这种不一样的写法. 你可以点开这个链接查 ...
- OO第三单元总结--根据JML写代码
一. JML语言 1. 理论基础 首先,JML不是JAVA的一部分,它是一群研究者为JAVA设计的扩展部分,但还没有得到官方的支持.因此,JAVA编译器并不支持JML,所以要想JML起作用,只能采用类 ...
- 你还不会Git?那就不要写代码了(一)
Git应用开发学习 如果你还不会使用Git,那就不要写代码了. 一旦你会使用了Git,就再也不想使用SVN了.永远也回不去了. Mac上使用Git,肯定离不开对Mac上的操作.就要使用常用的Linux ...
- 寻找写代码感觉(二)之 Spring Boot 项目属性配置
一.前言 写代码就和恋爱一样,有反馈就要趁热打铁,搞完了项目搭建,接下来就来搞搞项目配置. 二.IDEA设置 1.编码配置 这里所说的就是代码的编码格式,你可以不设置,但是可能要面临的是,很多未知的麻 ...
- 使用 .NET WinForm 开发所见即所得的 IDE 开发环境,实现不写代码直接生成应用程序
直接切入正题,这是我09年到11年左右业余时间编写的项目,最初的想法很简单,做一个能拖拖拽拽就直接生成应用程序的工具,不用写代码,把能想到的业务操作全部封装起来,通过配置的方式把这些业务操作组织起来运 ...
- ClownFish:比手写代码还快的通用数据访问层
http://www.cnblogs.com/fish-li/archive/2012/07/17/ClownFish.html 阅读目录 开始 ClownFish是什么? 比手写代码还快的执行速度 ...
- Markdown: 用写代码的思维写文档
作者:吴香伟 发表于 2014/08/07 版权声明:可以任意转载,转载时务必以超链接形式标明文章原始出处和作者信息以及版权声明 本文不讲解Markdown的语法规则,只关注它带来的好处以及我使用的方 ...
- 【腾讯Bugly干货分享】深入理解 ButterKnife,让你的程序学会写代码
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/578753c0c9da73584b025875 0.引子 话说我们做程序员的,都 ...
随机推荐
- 攻防世界 reverse BabyXor
BabyXor 2019_UNCTF 查壳 脱壳 dump 脱壳后 IDA静态分析 int main_0() { void *v0; // eax int v1; // ST5C_4 char ...
- vps-java环境配置
昨天准备用vps打一台反序列化的机子要java环境,java环境安装了但是javac一直用不了,鼓捣了一段时间把配置文件给搞没了,只好重装vps,找了很多帖子才搞定,为了防止下次继续出现这种情况又要重 ...
- 真会C#微信小程序的习题数据JSON文件下载链接
完全没有精力去维护了,所以小程序停掉,集中精力做一件事. 链接: https://pan.baidu.com/s/1xL45KxDzR5oEQM6nwBA5rw 提取码: qv6n
- pwn题命令行解题脚本
目录 脚本说明 脚本内容 使用 使用示例 参考与引用 脚本说明 这是专门为本地调试与远程答题准备的脚本,依靠命令行参数进行控制. 本脚本支持的功能有: 本地调试 开启tmux调试 设置gdb断点,支持 ...
- 如何在CMDB中落地应用的概念?
如何在CMDB中落地应用的概念? 我们前面讲了应用是整个微服务架构体系下运维的核心,而CMDB又是整个运维平台的基石.今天我就讲讲在CMDB中如何落地应用这个核心概念,以及如何建立应用集群分组的思路. ...
- Prometheus 配置文件中 metric_relabel_configs 配置--转载
Prometheus 配置文件中 metric_relabel_configs 配置 参考1:https://www.baidu.com/link?url=YfpBgnD1RoEthqXOL3Lgny ...
- 【设计模式】- 生成器模式(Builder)
生成器模式 建造者模式.Builder 生成器模式 也叫建造者模式,可以理解成可以分步骤创建一个复杂的对象.在该模式中允许你使用相同的创建代码生成不同类型和形式的对象. 生成器的结构模式 生成器(Bu ...
- Dapper, Ef core, Freesql 插入大量数据性能比较(二)
在上一篇文章中,我们比较出单表插入9999行数据,Dapper > EfCore > Freesql.在本文中,我们来看看级联插入 构建9999行数据 List<Entity> ...
- Spring Boot demo系列(十):Redis缓存
1 概述 本文演示了如何在Spring Boot中将Redis作为缓存使用,具体的内容包括: 环境搭建 项目搭建 测试 2 环境 Redis MySQL MyBatis Plus 3 Redis安装 ...
- SpringBoot开发秘籍 - 集成Graphql Query
概述 REST作为一种现代网络应用非常流行的软件架构风格受到广大WEB开发者的喜爱,在目前软件架构设计模式中随处可见REST的身影,但是随着REST的流行与发展,它的一个最大的缺点开始暴露出来: 在很 ...