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

思路分析

众所周知,利用反射可以在程序运行时获取到任一对象的类型、属性、参数、方法等,并加以调用,利用这些获取到的可以在程序运行时追加各种自定义的功能。以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,不过复杂程度比较高。

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

最新文章

  1. tornado + supervisor + nginx 的一点记录
  2. angularjs tips
  3. Effective C++ 笔记一 让自己习惯C++
  4. ajax实现分页
  5. IOS9新特性之Contacts联系人
  6. SVG文本
  7. Burp_用户名密码爆破
  8. Servlet 单例多线程【转】
  9. react使用过程中常见问题
  10. python3列表(元组)练习
  11. 阶段01Java基础day24多线程+GUI
  12. [Err] 1418 - This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creator【s
  13. TCP编程实践小结1
  14. IOS多线程处理
  15. StringDemo
  16. Ubuntu16.04下编译安装及运行单目ORBSLAM2
  17. 分布式tensorflow
  18. 个人总结(Alpha阶段)
  19. P2196 挖地雷
  20. DFT,可测试性设计--概念理解

热门文章

  1. 高仿京东到家APP引导页炫酷动画效果
  2. 使用 Kubernetes 扩展专用游戏服务器
  3. 使用 Github Actions artifact 在 workflow job 之间共享数据
  4. 写个小程序01 | 注册微信小程序
  5. java进阶(40)--wait与notify(生产者与消费者模式)
  6. Java开发工程师面试-Web基础与数据处理
  7. redis setNx原子锁
  8. 第1课:Linux操作系统基础【DevOps基础培训】
  9. 删除文件--rm
  10. [Azure Devops] 获取单元测试的代码覆盖率