.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,不過復雜程度比較高。

這樣批量生成代碼有點像活字印刷,雖然可以解決大量機械重復的工作,但靈活度還是不夠高,人腦還是無法替代的。

奇技淫巧,見笑了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM