從壹開始前後端分離 [.netCore 填坑 ] 三十二║ 四種方法快速實現專案的半自動化搭建
更新
1、更新小夥伴 @ 提出好點子:試試VS的外掛擴充套件:VSIX、ItemProject等,將T4模板給製作外掛,這裡先記下,有懂的小夥伴可以自己先試試,我會在以後更新。
2、感謝小夥伴 @的測試和指正,本文 T4 模板已經支援 Oracle
緣起
哈嘍大家週二好呀,這個國慶過的真是懶洋洋呀,不知道大家的學習動力咋樣了,剛一上班本人手中的專案也增加了,但是還是要抽出時間學習噠,為了不讓老闆大大天天催,所以更新會慢點兒 [ 哭笑 ] :bowtie:,不過在我的推薦下,公司下一個專案要我負責前後端分離,終於可以將這些派上用場了,中間的坑也會在以後的文章中,慢慢補充出來。
這幾天簡單想了想,還沒有想好要開什麼系列,就想到QQ群裡有小夥伴問的較多的一些問題以及前邊系列文章中的提到的,但是沒有深入講到的,再填一下坑,這樣才是完整的嘛,大家要是看到之前的有任何不清楚的,或者想擴充套件的,可以在群裡說一下,或者留言,我都會說到的。今天呢,就說說如果半自動化搭建專案,這個坑來自於之前的專案搭建文章《框架之六 || API專案整體搭建 6.1 倉儲模式》的伏筆之一 —— 如何使用 T4 模板搭建專案。當然,我們不僅是簡單說說實體類的建立,也會橫向和縱向的討論下,一個專案如何通過程式碼實現半自動化搭建。
說到這裡大家應該已經明白了這篇文章的寫作意圖,就是搭建專案的問題,相信大家已經開發了幾年了,都有自己的一套行之有效的辦法或者是經驗,當然這裡就不說付費的了,付費的框架何其多,比如迪西客,普元或者力軟等等,這都是好用的功能強大的付費框架,既然說技術,就不說付費的,咱們自己寫,這裡簡單概括下今天要說到的:
0、手動貼上複製 —— 可能現在還有小夥伴在用這個辦法,效率是真的很低,雖然我偶爾也用哈哈,這裡就不說了
1、動軟程式碼生成器 —— 這個我入門的時候使用的神器,用了好久
2、通過 VSTO 來編寫 Excel ,實現每一層的程式碼設計 —— 我沒用過,但是見過,一個不錯的思路
3、T4 模板 —— 這個是我這兩年遇到的又一個神器,也是一直使用的,強烈推薦的
4、SqlSuagr(ORM)—— 自封裝的一套邏輯,當然其他ORM也有,比如EF就很好
5、VSIX 擴充套件外掛 —— 新的處理方案
今天咱們這幾個方法都會說到,主要還是說一下通過 T4 模板,來把我們的整體框架半自動化起來。最終的效果截圖是這樣的:
框架截圖
一、動軟程式碼生成器搭建整體專案框架
相信很多的小夥伴應該使用過這個工具,還是很不錯的,我也是用了有一段時間,雖然很小,但是功能確實很豐富,無論是生成實體類,還是生成簡單三層,還是工廠模式等等,都可以使用,不僅支援單個表文件的操作,也支援整個資料庫的批量生成,這裡具體的不做敘述,因為不是本篇文章的重點,這裡簡單的說下使用方法以及效果圖:
1、安裝與使用
安裝還是很簡單的,就是普通的 next ,想用的可以試試,這裡就不做敘述了。
安裝成功後,我們可以看到,通過連線相應的伺服器,找到適當的資料庫,就可以操作了,主要還是通過一套 cmt 模板來生成對應的 .cs 類檔案,這些模板我們可以使用系統自帶的(比如工廠模式模板),也可以自定義編輯,以滿足我們不同專案的具體需求,我這裡就演示下,如何把我的部落格資料庫(還是咱們一直用的這個資料庫),生成簡單工程框架。
經過短暫的等待後,我們的全部框架就這麼出來了
當然這些字尾名等都可以配置,這裡就不細說了,咱們隨便看一下資料訪問層的內容:
/** 版本資訊模板在安裝目錄下,可自行修改。 * BlogArticle.cs * * 功 能: N/A * 類 名: BlogArticle * * Ver 變更日期 負責人 變更內容 * ─────────────────────────────────── * V0.01 2018/10/8 23:46:56 N/A 初版 * * Copyright (c) 2012 Maticsoft Corporation. All rights reserved. *┌──────────────────────────────────┐ *│ 此技術資訊為本公司機密資訊,未經本公司書面同意禁止向第三方披露. │ *│ 版權所有:動軟卓越(北京)科技有限公司 │ *└──────────────────────────────────┘ */ using System; using System.Data; using System.Text; using System.Data.SqlClient; using Maticsoft.IDAL; using Maticsoft.DBUtility;//Please add references namespace Maticsoft.SQLServerDAL { /// <summary> /// 資料訪問類:BlogArticle /// </summary> public partial class BlogArticle:IBlogArticle { public BlogArticle() {} #region BasicMethod /// <summary> /// 得到最大ID /// </summary> public int GetMaxId() { return DbHelperSQL.GetMaxID("bID", "BlogArticle"); } /// <summary> /// 是否存在該記錄 /// </summary> public bool Exists(int bID) { StringBuilder strSql=new StringBuilder(); strSql.Append("select count(1) from BlogArticle"); strSql.Append(" where [email protected]"); SqlParameter[] parameters = { new SqlParameter("@bID", SqlDbType.Int,4) }; parameters[0].Value = bID; return DbHelperSQL.Exists(strSql.ToString(),parameters); } /// <summary> /// 增加一條資料 /// </summary> public int Add(Maticsoft.Model.BlogArticle model) { StringBuilder strSql=new StringBuilder(); strSql.Append("insert into BlogArticle("); strSql.Append("bsubmitter,btitle,bcategory,bcontent,btraffic,bcommentNum,bUpdateTime,bCreateTime,bRemark)"); strSql.Append(" values ("); strSql.Append("@bsubmitter,@btitle,@bcategory,@bcontent,@btraffic,@bcommentNum,@bUpdateTime,@bCreateTime,@bRemark)"); strSql.Append(";select @@IDENTITY"); SqlParameter[] parameters = { new SqlParameter("@bsubmitter", SqlDbType.NVarChar,60), new SqlParameter("@btitle", SqlDbType.NVarChar,256), new SqlParameter("@bcategory", SqlDbType.NVarChar,-1), new SqlParameter("@bcontent", SqlDbType.Text), new SqlParameter("@btraffic", SqlDbType.Int,4), new SqlParameter("@bcommentNum", SqlDbType.Int,4), new SqlParameter("@bUpdateTime", SqlDbType.DateTime), new SqlParameter("@bCreateTime", SqlDbType.DateTime), new SqlParameter("@bRemark", SqlDbType.NVarChar,-1)}; parameters[0].Value = model.bsubmitter; parameters[1].Value = model.btitle; parameters[2].Value = model.bcategory; parameters[3].Value = model.bcontent; parameters[4].Value = model.btraffic; parameters[5].Value = model.bcommentNum; parameters[6].Value = model.bUpdateTime; parameters[7].Value = model.bCreateTime; parameters[8].Value = model.bRemark; object obj = DbHelperSQL.GetSingle(strSql.ToString(),parameters); if (obj == null) { return 0; } else { return Convert.ToInt32(obj); } } /// <summary> /// 更新一條資料 /// </summary> public bool Update(Maticsoft.Model.BlogArticle model) { StringBuilder strSql=new StringBuilder(); strSql.Append("update BlogArticle set "); strSql.Append("[email protected],"); strSql.Append("[email protected],"); strSql.Append("[email protected],"); strSql.Append("[email protected],"); strSql.Append("[email protected],"); strSql.Append("[email protected],"); strSql.Append("[email protected],"); strSql.Append("[email protected],"); strSql.Append("[email protected]"); strSql.Append(" where [email protected]"); SqlParameter[] parameters = { new SqlParameter("@bsubmitter", SqlDbType.NVarChar,60), new SqlParameter("@btitle", SqlDbType.NVarChar,256), new SqlParameter("@bcategory", SqlDbType.NVarChar,-1), new SqlParameter("@bcontent", SqlDbType.Text), new SqlParameter("@btraffic", SqlDbType.Int,4), new SqlParameter("@bcommentNum", SqlDbType.Int,4), new SqlParameter("@bUpdateTime", SqlDbType.DateTime), new SqlParameter("@bCreateTime", SqlDbType.DateTime), new SqlParameter("@bRemark", SqlDbType.NVarChar,-1), new SqlParameter("@bID", SqlDbType.Int,4)}; parameters[0].Value = model.bsubmitter; parameters[1].Value = model.btitle; parameters[2].Value = model.bcategory; parameters[3].Value = model.bcontent; parameters[4].Value = model.btraffic; parameters[5].Value = model.bcommentNum; parameters[6].Value = model.bUpdateTime; parameters[7].Value = model.bCreateTime; parameters[8].Value = model.bRemark; parameters[9].Value = model.bID; int rows=DbHelperSQL.ExecuteSql(strSql.ToString(),parameters); if (rows > 0) { return true; } else { return false; } } /// <summary> /// 刪除一條資料 /// </summary> public bool Delete(int bID) { StringBuilder strSql=new StringBuilder(); strSql.Append("delete from BlogArticle "); strSql.Append(" where [email protected]"); SqlParameter[] parameters = { new SqlParameter("@bID", SqlDbType.Int,4) }; parameters[0].Value = bID; int rows=DbHelperSQL.ExecuteSql(strSql.ToString(),parameters); if (rows > 0) { return true; } else { return false; } } /// <summary> /// 批量刪除資料 /// </summary> public bool DeleteList(string bIDlist ) { StringBuilder strSql=new StringBuilder(); strSql.Append("delete from BlogArticle "); strSql.Append(" where bID in ("+bIDlist + ") "); int rows=DbHelperSQL.ExecuteSql(strSql.ToString()); if (rows > 0) { return true; } else { return false; } } /// <summary> /// 得到一個物件實體 /// </summary> public Maticsoft.Model.BlogArticle GetModel(int bID) { StringBuilder strSql=new StringBuilder(); strSql.Append("select top 1 bID,bsubmitter,btitle,bcategory,bcontent,btraffic,bcommentNum,bUpdateTime,bCreateTime,bRemark from BlogArticle "); strSql.Append(" where [email protected]"); SqlParameter[] parameters = { new SqlParameter("@bID", SqlDbType.Int,4) }; parameters[0].Value = bID; Maticsoft.Model.BlogArticle model=new Maticsoft.Model.BlogArticle(); DataSet ds=DbHelperSQL.Query(strSql.ToString(),parameters); if(ds.Tables[0].Rows.Count>0) { return DataRowToModel(ds.Tables[0].Rows[0]); } else { return null; } } /// <summary> /// 得到一個物件實體 /// </summary> public Maticsoft.Model.BlogArticle DataRowToModel(DataRow row) { Maticsoft.Model.BlogArticle model=new Maticsoft.Model.BlogArticle(); if (row != null) { if(row["bID"]!=null && row["bID"].ToString()!="") { model.bID=int.Parse(row["bID"].ToString()); } if(row["bsubmitter"]!=null) { model.bsubmitter=row["bsubmitter"].ToString(); } if(row["btitle"]!=null) { model.btitle=row["btitle"].ToString(); } if(row["bcategory"]!=null) { model.bcategory=row["bcategory"].ToString(); } if(row["bcontent"]!=null) { model.bcontent=row["bcontent"].ToString(); } if(row["btraffic"]!=null && row["btraffic"].ToString()!="") { model.btraffic=int.Parse(row["btraffic"].ToString()); } if(row["bcommentNum"]!=null && row["bcommentNum"].ToString()!="") { model.bcommentNum=int.Parse(row["bcommentNum"].ToString()); } if(row["bUpdateTime"]!=null && row["bUpdateTime"].ToString()!="") { model.bUpdateTime=DateTime.Parse(row["bUpdateTime"].ToString()); } if(row["bCreateTime"]!=null && row["bCreateTime"].ToString()!="") { model.bCreateTime=DateTime.Parse(row["bCreateTime"].ToString()); } if(row["bRemark"]!=null) { model.bRemark=row["bRemark"].ToString(); } } return model; } /// <summary> /// 獲得資料列表 /// </summary> public DataSet GetList(string strWhere) { StringBuilder strSql=new StringBuilder(); strSql.Append("select bID,bsubmitter,btitle,bcategory,bcontent,btraffic,bcommentNum,bUpdateTime,bCreateTime,bRemark "); strSql.Append(" FROM BlogArticle "); if(strWhere.Trim()!="") { strSql.Append(" where "+strWhere); } return DbHelperSQL.Query(strSql.ToString()); } /// <summary> /// 獲得前幾行資料 /// </summary> public DataSet GetList(int Top,string strWhere,string filedOrder) { StringBuilder strSql=new StringBuilder(); strSql.Append("select "); if(Top>0) { strSql.Append(" top "+Top.ToString()); } strSql.Append(" bID,bsubmitter,btitle,bcategory,bcontent,btraffic,bcommentNum,bUpdateTime,bCreateTime,bRemark "); strSql.Append(" FROM BlogArticle "); if(strWhere.Trim()!="") { strSql.Append(" where "+strWhere); } strSql.Append(" order by " + filedOrder); return DbHelperSQL.Query(strSql.ToString()); } /// <summary> /// 獲取記錄總數 /// </summary> public int GetRecordCount(string strWhere) { StringBuilder strSql=new StringBuilder(); strSql.Append("select count(1) FROM BlogArticle "); if(strWhere.Trim()!="") { strSql.Append(" where "+strWhere); } object obj = DbHelperSQL.GetSingle(strSql.ToString()); if (obj == null) { return 0; } else { return Convert.ToInt32(obj); } } /// <summary> /// 分頁獲取資料列表 /// </summary> public DataSet GetListByPage(string strWhere, string orderby, int startIndex, int endIndex) { StringBuilder strSql=new StringBuilder(); strSql.Append("SELECT * FROM ( "); strSql.Append(" SELECT ROW_NUMBER() OVER ("); if (!string.IsNullOrEmpty(orderby.Trim())) { strSql.Append("order by T." + orderby ); } else { strSql.Append("order by T.bID desc"); } strSql.Append(")AS Row, T.* from BlogArticle T "); if (!string.IsNullOrEmpty(strWhere.Trim())) { strSql.Append(" WHERE " + strWhere); } strSql.Append(" ) TT"); strSql.AppendFormat(" WHERE TT.Row between {0} and {1}", startIndex, endIndex); return DbHelperSQL.Query(strSql.ToString()); } /* /// <summary> /// 分頁獲取資料列表 /// </summary> public DataSet GetList(int PageSize,int PageIndex,string strWhere) { SqlParameter[] parameters = { new SqlParameter("@tblName", SqlDbType.VarChar, 255), new SqlParameter("@fldName", SqlDbType.VarChar, 255), new SqlParameter("@PageSize", SqlDbType.Int), new SqlParameter("@PageIndex", SqlDbType.Int), new SqlParameter("@IsReCount", SqlDbType.Bit), new SqlParameter("@OrderType", SqlDbType.Bit), new SqlParameter("@strWhere", SqlDbType.VarChar,1000), }; parameters[0].Value = "BlogArticle"; parameters[1].Value = "bID"; parameters[2].Value = PageSize; parameters[3].Value = PageIndex; parameters[4].Value = 0; parameters[5].Value = 0; parameters[6].Value = strWhere; return DbHelperSQL.RunProcedure("UP_GetRecordByPage",parameters,"ds"); }*/ #endregion BasicMethod #region ExtensionMethod #endregion ExtensionMethod } }View Code
提示:如果你不想下載改軟體,又想看看具體的程式碼,我已經提交到咱們的git 了,在wwwroot 資料夾中,大家可以看看
截圖
開啟看一下,基本的 CURD 方法都有,也有一些儲存過程的呼叫等,而且可以自定義擴充套件,不得不說是一個神器,如果要開發 ADO.NET 多層,這個方法也不失為一個好的方案。
優點大家都看到了,快速,內容簡潔豐富,各種儲存過程分頁方法很方便,不言而喻;
但是缺點也是有的,最大的就是不能像其他ORM框架那樣,實現 Code First ,只能單純的講資料庫資訊提取出來;而且在編輯模板方面也沒有 T4 那麼容易操作,修改模板程式碼不是很方便,學習的難度稍微大了一些。
總結:如果不想用 T4 模板手動寫模板程式碼,又不想引入框架,只想用 ADO.NET 搭建多層框架,動軟程式碼生成器是一個不錯的選擇。
二、通過 VSTO 編寫 Excel 來搭建整體專案框架
1、什麼是VSTO?
VSTO(Visual Studio Tools For Office)就像名字一樣,是Visual Studio開發Office的工具集。只是一套工具,用於簡化.NET的Office開發。能夠生成com元件或者標準dll的都能開發Office,比如c/c++,VB6,Delphi等等。原生.NET當然也可以。微軟覺得.net開發Office不夠方便,VSTO便誕生了。
1.決定要用.NET開發Office
2.在所有.NET開發Office的途徑(原始,各種工具)中,選擇了VSTO
2、如何新建一個 VSTO 專案
QQ群裡有一個小夥伴大神,他通過 VSTO 來寫的一個框架,其實就是通過命令來將固定的格式進行輸出,這裡盜用一下他的圖,應該還好吧,因為不知道他的部落格園賬號,就先不@他了,個人感覺這個還是很不錯的,看著很溜
這個方法我現在也在學,不過只是做一個知識擴充套件來用,好處是,我們自己做一個Excel 工具後,走到哪裡都可以使用,還很方便,自己隨便自定義,不用受專案或者資料庫的環境影響,特別是做展示的時候,很直觀,比如開專案研討會的時候,幾個人討論資料庫表結構呀,生成的方法呀,總不能一個個軟體都開啟吧;
但是也有一些問題,畢竟侷限性有些強,比如好像不能直接操作資料庫,在大資料結構中,效率不高,而且不能直接生成檔案,需要拷貝操作等;
通過上邊兩個栗子可以看出來,一個是連線資料庫快捷,檔案生成方便;一個自定義邏輯性強,展示直觀,那有沒有辦法可以將兩個優點結合起來呢,沒錯,就是T4模板了,耐心看完下邊的講解,你會發現很強大。
三、通過 T4 模板搭建整體專案框架
1、什麼是 T4 模板
T4 (Text Template Transformation Toolkit) 是微軟官方在 VisualStudio 2008+ 中開始使用的程式碼生成引擎。在 Visual Studio 中,“T4 文字模板”是由一些文字塊和控制邏輯組成的混合模板,它可以生成文字檔案。 在 Visual C# 或 Visual Basic 中,控制邏輯編寫為程式程式碼的片段。生成的檔案可以是任何型別的文字,例如網頁、資原始檔或任何語言的程式原始碼。現在的VS中只要與程式碼生成相關的場景基本上都能找T4的身影,比如MVC的檢視模板,Entity Framwork的DataContext模板等等。
2、在專案中通過T4實現資料庫生成實體類
這裡就不具體講解 T4 語法了,大家可以自行學習,其實很簡單,主要還是 C# 程式碼,下邊你看過之後就能懂了,咱們首先先實現之前留下的一個伏筆 —— 將我們的資料庫表利用T4 模板生成實體類,也就是 DbFirst。
1、首先在我們的專案中,新建一個類庫 Blog.Core.FrameWork
2、在該類庫下,新建資料夾 Blog.Core.FrameWork.Entity,用於單獨存放我們的模板以及生成的實體類檔案
3、在類庫的根目錄新建 ModelAuto.ttinclude 模板檔案,用來控制我們的檔案生成配置
可以直接新建一個文字文件,然後重新命名即可
提示:初始狀態下,程式碼不是高亮的,大家可以安裝外掛:下載地址,不過個人感覺並不是很好用。
//引入名稱空間 <#@ assembly name="System.Core"#> <#@ assembly name="EnvDTE"#> <#@ import namespace="System.Collections.Generic"#> <#@ import namespace="System.IO"#> <#@ import namespace="System.Text"#> <#@ import namespace="Microsoft.VisualStudio.TextTemplating"#> <#+ //定義管理者 manager 實體類 class Manager { //定義一個 block 塊,主要是應用在批量生產中 public struct Block { public String Name; public int Start, Length; } public List<Block> blocks = new List<Block>(); public Block currentBlock; public Block footerBlock = new Block(); public Block headerBlock = new Block(); public ITextTemplatingEngineHost host; public ManagementStrategy strategy; public StringBuilder template; public String OutputPath { get; set; } //建構函式,包含 host主機,模板,輸出路徑,建立管理策略 public Manager(ITextTemplatingEngineHost host, StringBuilder template, bool commonHeader) { this.host = host; this.template = template; OutputPath = String.Empty; strategy = ManagementStrategy.Create(host); } //開闢一個 block 塊 public void StartBlock(String name) { currentBlock = new Block { Name = name, Start = template.Length }; } public void StartFooter() { footerBlock.Start = template.Length; } public void EndFooter() { footerBlock.Length = template.Length - footerBlock.Start; } public void StartHeader() { headerBlock.Start = template.Length; } public void EndHeader() { headerBlock.Length = template.Length - headerBlock.Start; } public void EndBlock() { currentBlock.Length = template.Length - currentBlock.Start; blocks.Add(currentBlock); } //定義程序,用來將所有的 blocks 塊執行出來 public void Process(bool split) { String header = template.ToString(headerBlock.Start, headerBlock.Length); String footer = template.ToString(footerBlock.Start, footerBlock.Length); blocks.Reverse(); foreach(Block block in blocks) {//遍歷 //輸出檔案 String fileName = Path.Combine(OutputPath, block.Name); if (split) { String content = header + template.ToString(block.Start, block.Length) + footer; strategy.CreateFile(fileName, content); template.Remove(block.Start, block.Length); } else { strategy.DeleteFile(fileName); } } } } //定義管理策略類 class ManagementStrategy { internal static ManagementStrategy Create(ITextTemplatingEngineHost host) { return (host is IServiceProvider) ? new VSManagementStrategy(host) : new ManagementStrategy(host); } internal ManagementStrategy(ITextTemplatingEngineHost host) { } internal virtual void CreateFile(String fileName, String content) { File.WriteAllText(fileName, content); } internal virtual void DeleteFile(String fileName) { if (File.Exists(fileName)) File.Delete(fileName); } } class VSManagementStrategy : ManagementStrategy { private EnvDTE.ProjectItem templateProjectItem; internal VSManagementStrategy(ITextTemplatingEngineHost host) : base(host) { IServiceProvid