想要用代码写代码,肯定是绕不开反射的。反射的概念相比都不陌生,只是应用多少就因人而异,今天分享一个代码生成器的思路,仅供参考,不要过分依赖哦。

思路分析

众所周知,利用反射可以在程序运行时获取到任一对象的类型、属性、参数、方法等,并加以调用,利用这些获取到的可以在程序运行时追加各种自定义的功能。以CRUD为例,我们可以利用反射获取到所有的Model并编写代码模板,最终达成用代码生成代码的结果。思路既然能走通,开搞开搞~

编码阶段

首先要确定生成哪些代码。CRUD每天都在做,但大部分都一样,这就导致每天有些时间都在板砖,毫无意义。所以我们需要自动生成全库的CRUD代码。

首先要写一个CRUD的模板代码,考虑到ORM框架太多,这里就以SqlSugar为例:

  1.      [HttpGet]
  2. public async Task<IActionResult> GetList(int index = 1, int size = 15)
  3. {
  4. RefAsync<int> count = 0;
  5.  
  6. return PageMsg(await _sqlHelper.DB
  7. .Queryable<Models.AD>()
  8. .OrderBy(x => x.Weight)
  9. .ToPageListAsync(index, size, count), count.Value);
  10. }
  11.  
  12. [HttpPost, Role("添加广告")]
  13. public async Task<IActionResult> Add([FromForm] Models.AD ad)
  14. {
  15. Models.AD add = await _sqlHelper.DB
  16. .Insertable(ad)
  17. .RemoveDataCache()
  18. .ExecuteReturnEntityAsync();
  19.  
  20. return Ok(add);
  21. }
  22.  
  23. [HttpPut, Role("修改广告")]
  24. public async Task<IActionResult> Cag([FromForm] Models.AD ad)
  25. {
           int result = await _sqlHelper.DB
  26. .Updateable(ad)
  27. .RemoveDataCache()
  28. .ExecuteCommandAsync();
           
           return YesOrNo(result > 0);
  29. }
  30.  
  31. [HttpDelete]
  32. public async Task<IActionResult> Delete([FromForm] int id)
  33. {
           int result = await _sqlHelper.DB
  34. .Deleteable<Models.AD>()
  35. .Where(x => x.ID == id)
  36. .ExecuteCommandAsync();

  37.        return YesOrNo(result > 0);
  38. }

能看出这是一组操作AD类的CRUD,接下来就是需要把上面的模板封装起来,AD这种表的名称作为变量输入即可,如果想控制代码文件的路径,也可以作为参数传入。完整版:

  1.      public void Make(string tableName, string path)
  2. {
  3. if (!Directory.Exists(path))
  4. Directory.CreateDirectory(path);
  5.  
  6. using FileStream fs = new FileStream($"{path}/{tableName}Controller.cs", FileMode.Append);
  7.  
  8. System.Text.StringBuilder sb = new System.Text.StringBuilder();
  9. sb.Append("using System.Threading.Tasks;");
  10. sb.Append("\n");
  11. sb.Append("using XXXX.Services.DB;");
  12. sb.Append("\n");
  13. sb.Append("using Microsoft.AspNetCore.Authorization;");
  14. sb.Append("\n");
  15. sb.Append("using Microsoft.AspNetCore.Mvc;");
  16. sb.Append("\n");
  17. sb.Append("using SqlSugar;");
  18. sb.Append("\n");
  19. sb.Append("namespace XXXX.Controllers.v1");
  20. sb.Append("\n");
  21. sb.Append("{");
  22. sb.Append("\n");
  23. sb.Append(" [ApiController, Route(\"v1/[controller]/[action]\"), Authorize]");
  24. sb.Append("\n");
  25. sb.Append($" public class {tableName}Controller : BaseController");
  26. sb.Append("\n");
  27. sb.Append(" {");
  28. sb.Append("\n");
  29. sb.Append(" private readonly SqlHelper _sqlHelper;");
  30. sb.Append("\n");
  31. sb.Append($" public {tableName}Controller(SqlHelper sqlHelper)");
  32. sb.Append("\n");
  33. sb.Append(" {");
  34. sb.Append("\n");
  35. sb.Append(" _sqlHelper = sqlHelper;");
  36. sb.Append("\n");
  37. sb.Append(" }");
  38. sb.Append("\n");
  39. sb.Append(" [HttpGet]");
  40. sb.Append("\n");
  41. sb.Append($" public async Task<IActionResult> GetList(int index = 1, int size = 15)");
  42. sb.Append("\n");
  43. sb.Append(" {");
  44. sb.Append("\n");
  45. sb.Append($" RefAsync<int> count = 0;");
  46. sb.Append("\n");
  47. sb.Append(" return Ok(new{");
  48. sb.Append("\n");
  49. sb.Append($" rows = await _sqlHelper.DB.Queryable<Models.{tableName}>().ToPageListAsync(index, size, count),");
  50. sb.Append("\n");
  51. sb.Append(" total = count.Value");
  52. sb.Append("\n");
  53. sb.Append(" });");
  54. sb.Append("\n");
  55. sb.Append(" }");
  56. sb.Append("\n");
  57. sb.Append(" [HttpPost]");
  58. sb.Append("\n");
  59. sb.Append($" public async Task<IActionResult> Add([FromForm] Models.{tableName} {tableName.ToLower()})");
  60. sb.Append("\n");
  61. sb.Append(" {");
  62. sb.Append("\n");
  63. sb.Append($" Models.{tableName} add = await _sqlHelper.DB.Insertable({tableName.ToLower()}).RemoveDataCache().ExecuteReturnEntityAsync();");
  64. sb.Append("\n");
  65. sb.Append(" return Ok(add);");
  66. sb.Append("\n");
  67. sb.Append(" }");
  68. sb.Append("\n");
  69. sb.Append(" [HttpPut]");
  70. sb.Append("\n");
  71. sb.Append($" public async Task<IActionResult> Cag([FromForm] Models.{tableName} {tableName.ToLower()})");
  72. sb.Append("\n");
  73. sb.Append(" {");
  74. sb.Append("\n");
  75. sb.Append($" int result = await _sqlHelper.DB.Updateable({tableName.ToLower()}).RemoveDataCache().ExecuteCommandAsync();");
  76. sb.Append("\n");
  77. sb.Append(" return YesOrNo(result>0);");
  78. sb.Append("\n");
  79. sb.Append(" }");
  80. sb.Append("\n");
  81. sb.Append(" [HttpDelete]");
  82. sb.Append("\n");
  83. sb.Append($" public async Task<IActionResult> Cag([FromForm] int id)");
  84. sb.Append("\n");
  85. sb.Append(" {");
  86. sb.Append("\n");
  87. sb.Append($" int result = await _sqlHelper.DB.Deleteable<Models.{tableName}>().Where(x => x.ID == id).ExecuteCommandAsync();");
  88. sb.Append("\n");
  89. sb.Append(" return YesOrNo(result>0);");
  90. sb.Append("\n");
  91. sb.Append(" }");
  92. sb.Append("\n");
  93. sb.Append(" }");
  94. sb.Append("\n");
  95. sb.Append("}");
  96.  
  97. using StreamWriter sw = new StreamWriter(fs);
  98.  
  99. sw.WriteLine(sb.ToString());
  100. }

为了方便理解,刻意美化了一下。

OK,代码模板搞好了,该从哪里拿到所有的Model呢?思路是先加载程序集,然后找到存放Model的命名空间,然后找到命名空间下面所有符合条件的Class,然后就可以拿到具体的名称,从而调用代码模板进行文件生成,像这样:

  1.        var assembly = Assembly.Load("程序集名称");
  2.  
  3. var types = assembly.ExportedTypes.Where(a => a.FullName.Contains("Model所处的命名空间")).ToList();

  4.        //指定
  5. string path = $"{Directory.GetCurrentDirectory()}/autoCode";
  6.  
  7. foreach (var item in types)
  8. {
  9. string tableName = item.FullName.Split('.')[^1];
  10.  
  11. Make(tableName, path);
  12. }

接下来直接运行,走完以后的结果是这样的:

随便打开一个文件,是这样的:

效果还是不错的。

如果需要更加精细的控制,可以预先在Model设置特性,然后通过判断特性是否存在来决定如何生成代码,举个栗子:

  1.        var assembly = Assembly.Load("程序集名称");
  2.  
  3. var types = assembly.ExportedTypes.Where(a => a.FullName.Contains("Model所处的命名空间")).ToList();
  4.  
  5.        //指定
  6. string path = $"{Directory.GetCurrentDirectory()}/autoCode";
  7.  
  8. foreach (var item in types)
  9. {
              //获取所有公开属性
  10. foreach (var prop in item.GetProperties())
  11.           {
       MyAttribute attr = method.GetCustomAttribute(typeof(MyAttribute), true) as MyAttribute;
                if(attr is null)
                {
                  //进入这里代表该属性没有附加 MyAttribute
                }
    }
  12. }

当然,还可以获取MyAttribute的属性拿过来进行判断等等。而实际上很多ORM框架也是用类似手段实现Code First、DB First,不过复杂程度比较高。

这样批量生成代码有点像活字印刷,虽然可以解决大量机械重复的工作,但灵活度还是不够高;人脑还是无法替代的,奇技淫巧,见笑了。

.Net Core——用代码写代码?的更多相关文章

  1. Docker & ASP.NET Core (1):把代码连接到容器

    和这种蛋糕一样,Docker的容器和镜像也是使用类似的分层文件系统构建而成的. 这样做的好处就是可以节省硬盘空间,也利于复用等等.因为Docker基于镜像创建容器的时候,其镜像是共享的:而且镜像里面的 ...

  2. .NET/C# 异常处理:写一个空的 try 块代码,而把重要代码写到 finally 中

    不知你是否见过 try { } finally { } 代码中,try 块留空,而只往 finally 中写代码的情况呢?这种写法有其特殊的目的. 本文就来说说这种不一样的写法. 你可以点开这个链接查 ...

  3. OO第三单元总结--根据JML写代码

    一. JML语言 1. 理论基础 首先,JML不是JAVA的一部分,它是一群研究者为JAVA设计的扩展部分,但还没有得到官方的支持.因此,JAVA编译器并不支持JML,所以要想JML起作用,只能采用类 ...

  4. 你还不会Git?那就不要写代码了(一)

    Git应用开发学习 如果你还不会使用Git,那就不要写代码了. 一旦你会使用了Git,就再也不想使用SVN了.永远也回不去了. Mac上使用Git,肯定离不开对Mac上的操作.就要使用常用的Linux ...

  5. 寻找写代码感觉(二)之 Spring Boot 项目属性配置

    一.前言 写代码就和恋爱一样,有反馈就要趁热打铁,搞完了项目搭建,接下来就来搞搞项目配置. 二.IDEA设置 1.编码配置 这里所说的就是代码的编码格式,你可以不设置,但是可能要面临的是,很多未知的麻 ...

  6. 使用 .NET WinForm 开发所见即所得的 IDE 开发环境,实现不写代码直接生成应用程序

    直接切入正题,这是我09年到11年左右业余时间编写的项目,最初的想法很简单,做一个能拖拖拽拽就直接生成应用程序的工具,不用写代码,把能想到的业务操作全部封装起来,通过配置的方式把这些业务操作组织起来运 ...

  7. ClownFish:比手写代码还快的通用数据访问层

    http://www.cnblogs.com/fish-li/archive/2012/07/17/ClownFish.html 阅读目录 开始 ClownFish是什么? 比手写代码还快的执行速度 ...

  8. Markdown: 用写代码的思维写文档

    作者:吴香伟 发表于 2014/08/07 版权声明:可以任意转载,转载时务必以超链接形式标明文章原始出处和作者信息以及版权声明 本文不讲解Markdown的语法规则,只关注它带来的好处以及我使用的方 ...

  9. 【腾讯Bugly干货分享】深入理解 ButterKnife,让你的程序学会写代码

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/578753c0c9da73584b025875 0.引子 话说我们做程序员的,都 ...

随机推荐

  1. c++ 反汇编 构造函数和析构函数

    构造函数和析构函数出现的时机 局部对象 109: // 局部对象定义调用构造函数 110: 111: CNumber Number; 00C8A37D 8D 4D EC lea ecx,[Number ...

  2. 深入理解Java并发框架AQS系列(三):独占锁(Exclusive Lock)

    一.前言 优秀的源码就在那里 经过了前面两章的铺垫,终于要切入正题了,本章也是整个AQS的核心之一 从本章开始,我们要精读AQS源码,在欣赏它的同时也要学会质疑它.当然本文不会带着大家逐行过源码(会有 ...

  3. 论Redis分布式锁的正确使用姿势

    前言 日常开发中,秒杀下单.抢红包等等业务场景,都需要用到分布式锁.而Redis非常适合作为分布式锁使用.本文将分七个方案展开,跟大家探讨Redis分布式锁的正确使用方式.如果有不正确的地方,欢迎大家 ...

  4. Clang Static Analyzer-使用手册-编写Checker框架

    Clang Static Analyzer-使用手册-编写Checker Checker是这个工具的灵魂 有了checker才可以检查你的代码 相当于就是CSA通过checker定义的检查方法去检查代 ...

  5. Java读取图片exif信息实现图片方向自动纠正

    起因 一个对试卷进行OCR识别需求,需要实现一个功能,一个章节下的题目图片需要上下拼接合成一张大图,起初写了一个工具实现图片的合并,程序一直很稳定的运行着,有一反馈合成的图片方向不对,起初怀疑是本身图 ...

  6. Python数据分析入门(十七):绘制条形图

    条形图的绘制方式跟折线图非常的类似,只不过是换成了plt.bar方法.plt.bar方法有以下常用参数: x:一个数组或者列表,代表需要绘制的条形图的x轴的坐标点. height:一个数组或者列表,代 ...

  7. 鹏城杯_2018_treasure

    鹏城杯_2018_treasure 首先检查一下保护: IDA分析 我们先来看看settreasure()函数 申请了两个内存空间,并往sea中复制了shellcode 看看这个shellcode,不 ...

  8. 在Visual Studio 中使用git——给Visual Studio安装 git插件(二)

    在Visual Studio 中使用git--什么是Git(一) 第二部分: 给Visual Studio安装 git插件 如果要使用 git 进行版本管理,其实使用 git 命令行工具就完全足够了, ...

  9. kali,创建/修改root密码,进入单元模式

    第一次发博客,从入门开始,从爱好变为工作 本人学习渗透不到一个月,如果有大佬看到此文章请不要喷,毕竟萌新不懂事,哈哈~ kali是一种非常强大的渗透工具 先说一下kali中的三个符号把   ~    ...

  10. 王炸!!IDEA 2021.1 推出语音、视频功能,边写代码边聊天,我真的服了…

    IDEA 2020.3 刚没用多久,2021.1 又陆续给我推送更新了: 启动就提醒更新,麻烦,那不如更新下,体验下新版本. 如上图所示,2021.1 更新了 9 个新特性,下面栈长会一一体验给大家介 ...