緣起
哈嘍大家周四好呀,這個(gè)國慶過的真是懶洋洋呀,不知道大家的學(xué)習(xí)動(dòng)力咋樣了,剛一上班本人手中的項(xiàng)目也增加了,但是還是要抽出時(shí)間學(xué)習(xí)噠,為了不讓老板大大天天催,所以更新會(huì)慢點(diǎn)兒 [ 哭笑 ] :bowtie:,不過在我的推薦下,公司下一個(gè)項(xiàng)目要我負(fù)責(zé)前后端分離,終于可以將這些派上用場(chǎng)了,中間的坑也會(huì)在以后的文章中,慢慢補(bǔ)充出來。
這幾天簡單想了想,還沒有想好要開什么系列,就想到QQ群里有小伙伴問的較多的一些問題以及前邊系列文章中的提到的,但是沒有深入講到的,再填一下坑,這樣才是完整的嘛,大家要是看到之前的有任何不清楚的,或者想擴(kuò)展的,可以在群里說一下,或者留言,我都會(huì)說到的。今天呢,就說說如果半自動(dòng)化搭建項(xiàng)目,這個(gè)坑來自于之前的項(xiàng)目搭建文章《框架之六 || API項(xiàng)目整體搭建 6.1 倉儲(chǔ)模式》的伏筆之一 —— 如何使用 T4 模板搭建項(xiàng)目。當(dāng)然,我們不僅是簡單說說實(shí)體類的創(chuàng)建,也會(huì)橫向和縱向的討論下,一個(gè)項(xiàng)目如何通過代碼實(shí)現(xiàn)半自動(dòng)化搭建。
說到這里大家應(yīng)該已經(jīng)明白了這篇文章的寫作意圖,就是搭建項(xiàng)目的問題,相信大家已經(jīng)開發(fā)了幾年了,都有自己的一套行之有效的辦法或者是經(jīng)驗(yàn),當(dāng)然這里就不說付費(fèi)的了,付費(fèi)的框架何其多,比如迪西客,普元或者力軟等等,這都是好用的功能強(qiáng)大的付費(fèi)框架,既然說技術(shù),就不說付費(fèi)的,咱們自己寫,這里簡單概括下今天要說到的:
0、手動(dòng)粘貼復(fù)制 —— 可能現(xiàn)在還有小伙伴在用這個(gè)辦法,效率是真的很低,雖然我偶爾也用哈哈,這里就不說了
1、動(dòng)軟代碼生成器 —— 這個(gè)我入門的時(shí)候使用的神器,用了好久
2、通過 VSTO 來編寫 Excel ,實(shí)現(xiàn)每一層的代碼設(shè)計(jì) —— 我沒用過,但是見過,一個(gè)不錯(cuò)的思路
3、T4 模板 —— 這個(gè)是我這兩年遇到的又一個(gè)神器,也是一直使用的,強(qiáng)烈推薦的
4、SqlSuagr(ORM)—— 自封裝的一套邏輯,當(dāng)然其他ORM也有,比如EF就很好
今天咱們這幾個(gè)方法都會(huì)說到,主要還是說一下通過 T4 模板,來把我們的整體框架半自動(dòng)化起來。最終的效果截圖是這樣的:
框架截圖

一、動(dòng)軟代碼生成器搭建整體項(xiàng)目框架
相信很多的小伙伴應(yīng)該使用過這個(gè)工具,還是很不錯(cuò)的,我也是用了有一段時(shí)間,雖然很小,但是功能確實(shí)很豐富,無論是生成實(shí)體類,還是生成簡單三層,還是工廠模式等等,都可以使用,不僅支持單個(gè)表文件的操作,也支持整個(gè)數(shù)據(jù)庫的批量生成,這里具體的不做敘述,因?yàn)椴皇潜酒恼碌闹攸c(diǎn),這里簡單的說下使用方法以及效果圖:
1、安裝與使用
地址:http://www.maticsoft.com/download.aspx
安裝還是很簡單的,就是普通的 next ,想用的可以試試,這里就不做敘述了。
安裝成功后,我們可以看到,通過連接相應(yīng)的服務(wù)器,找到適當(dāng)?shù)臄?shù)據(jù)庫,就可以操作了,主要還是通過一套 cmt 模板來生成對(duì)應(yīng)的 .cs 類文件,這些模板我們可以使用系統(tǒng)自帶的(比如工廠模式模板),也可以自定義編輯,以滿足我們不同項(xiàng)目的具體需求,我這里就演示下,如何把我的博客數(shù)據(jù)庫(還是咱們一直用的這個(gè)數(shù)據(jù)庫),生成簡單工程框架。

經(jīng)過短暫的等待后,我們的全部框架就這么出來了

當(dāng)然這些后綴名等都可以配置,這里就不細(xì)說了,大家下載隨便看一下數(shù)據(jù)訪問層的內(nèi)容。
提示:如果你不想下載改軟件,又想看看具體的代碼,我已經(jīng)提交到咱們的git 了,在wwwroot 文件夾中,大家可以看看
截圖

打開看一下,基本的 CURD 方法都有,也有一些存儲(chǔ)過程的調(diào)用等,而且可以自定義擴(kuò)展,不得不說是一個(gè)神器,如果要開發(fā) ADO.NET 多層,這個(gè)方法也不失為一個(gè)好的方案。
優(yōu)點(diǎn)大家都看到了,快速,內(nèi)容簡潔豐富,各種存儲(chǔ)過程分頁方法很方便,不言而喻;
但是缺點(diǎn)也是有的,最大的就是不能像其他ORM框架那樣,實(shí)現(xiàn) Code First ,只能單純的講數(shù)據(jù)庫信息提取出來;而且在編輯模板方面也沒有 T4 那么容易操作,修改模板代碼不是很方便,學(xué)習(xí)的難度稍微大了一些。
總結(jié):如果不想用 T4 模板手動(dòng)寫模板代碼,又不想引入框架,只想用 ADO.NET 搭建多層框架,動(dòng)軟代碼生成器是一個(gè)不錯(cuò)的選擇。
二、通過 VSTO 編寫 Excel 來搭建整體項(xiàng)目框架
1、什么是VSTO?
VSTO(Visual Studio Tools For Office)就像名字一樣,是Visual Studio開發(fā)Office的工具集。只是一套工具,用于簡化.NET的Office開發(fā)。能夠生成com組件或者標(biāo)準(zhǔn)dll的都能開發(fā)Office,比如c/c++,VB6,Delphi等等。原生.NET當(dāng)然也可以。微軟覺得.net開發(fā)Office不夠方便,VSTO便誕生了。
1.決定要用.NET開發(fā)Office
2.在所有.NET開發(fā)Office的途徑(原始,各種工具)中,選擇了VSTO
2、如何新建一個(gè) VSTO 項(xiàng)目
網(wǎng)上有一個(gè)栗子,大家可以參看著自己玩一玩:https://www.cnblogs.com/crhdyl/p/5289307.html。 對(duì)于這個(gè)方法,大家自己玩一玩就好了,這里就不說具體的教程了,畢竟不是本文重點(diǎn)。
QQ群里有一個(gè)小伙伴大神,他通過 VSTO 來寫的一個(gè)框架,其實(shí)就是通過命令來將固定的格式進(jìn)行輸出,這里盜用一下他的圖,應(yīng)該還好吧,因?yàn)椴恢浪牟┛蛨@賬號(hào),就先不@他了,個(gè)人感覺這個(gè)還是很不錯(cuò)的,看著很溜

這個(gè)方法我現(xiàn)在也在學(xué),不過只是做一個(gè)知識(shí)擴(kuò)展來用,好處是,我們自己做一個(gè)Excel 工具后,走到哪里都可以使用,還很方便,自己隨便自定義,不用受項(xiàng)目或者數(shù)據(jù)庫的環(huán)境影響,特別是做展示的時(shí)候,很直觀,比如開項(xiàng)目研討會(huì)的時(shí)候,幾個(gè)人討論數(shù)據(jù)庫表結(jié)構(gòu)呀,生成的方法呀,總不能一個(gè)個(gè)軟件都打開吧;
但是也有一些問題,畢竟局限性有些強(qiáng),比如好像不能直接操作數(shù)據(jù)庫,在大數(shù)據(jù)結(jié)構(gòu)中,效率不高,而且不能直接生成文件,需要拷貝操作等;
通過上邊兩個(gè)栗子可以看出來,一個(gè)是連接數(shù)據(jù)庫快捷,文件生成方便;一個(gè)自定義邏輯性強(qiáng),展示直觀,那有沒有辦法可以將兩個(gè)優(yōu)點(diǎn)結(jié)合起來呢,沒錯(cuò),就是T4模板了,耐心看完下邊的講解,你會(huì)發(fā)現(xiàn)很強(qiáng)大。
三、通過 T4 模板搭建整體項(xiàng)目框架
1、什么是 T4 模板
** T4 (Text Template Transformation Toolkit) 是微軟官方在 VisualStudio 2008+ 中開始使用的代碼生成引擎。在 Visual Studio 中,“T4 文本模板”是由一些文本塊和控制邏輯組成的混合模板,它可以生成文本文件。 在 Visual C# 或 Visual Basic 中,控制邏輯編寫為程序代碼的片段。生成的文件可以是任何類型的文本,例如網(wǎng)頁、資源文件或任何語言的程序源代碼。現(xiàn)在的VS中只要與代碼生成相關(guān)的場(chǎng)景基本上都能找T4的身影,比如MVC的視圖模板,Entity Framwork的DataContext**模板等等。
2、在項(xiàng)目中通過T4實(shí)現(xiàn)數(shù)據(jù)庫生成實(shí)體類
這里就不具體講解 T4 語法了,大家可以自行學(xué)習(xí),其實(shí)很簡單,主要還是 C# 代碼,下邊你看過之后就能懂了,咱們首先先實(shí)現(xiàn)之前留下的一個(gè)伏筆 —— 將我們的數(shù)據(jù)庫表利用T4 模板生成實(shí)體類,也就是 DbFirst。
**1、首先在我們的項(xiàng)目中,新建一個(gè)類庫 Blog.Core.FrameWork **
2、在該類庫下,新建文件夾 Blog.Core.FrameWork.Entity,用于單獨(dú)存放我們的模板以及生成的實(shí)體類文件

3、在類庫的根目錄新建 ModelAuto.ttinclude 模板文件,用來控制我們的文件生成配置
可以直接新建一個(gè)文本文檔,然后重命名即可
提示:初始狀態(tài)下,代碼不是高亮的,大家可以安裝插件:下載地址,不過個(gè)人感覺并不是很好用。
//引入命名空間
<#@ 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 實(shí)體類
class Manager
{ //定義一個(gè) block 塊,主要是應(yīng)用在批量生產(chǎn)中
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; } //構(gòu)造函數(shù),包含 host主機(jī),模板,輸出路徑,創(chuàng)建管理策略
public Manager(ITextTemplatingEngineHost host, StringBuilder template, bool commonHeader) { this.host = host; this.template = template;
OutputPath = String.Empty;
strategy = ManagementStrategy.Create(host);
} //開辟一個(gè) 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);
} //定義進(jìn)程,用來將所有的 blocks 塊執(zhí)行出來
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) {
IServiceProvider hostServiceProvider = (IServiceProvider)host; if (hostServiceProvider == null) throw new ArgumentNullException("Could not obtain hostServiceProvider");
EnvDTE.DTE dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE)); if (dte == null) throw new ArgumentNullException("Could not obtain DTE from host");
templateProjectItem = dte.Solution.FindProjectItem(host.TemplateFile);
} //創(chuàng)建文件
internal override void CreateFile(String fileName, String content) { base.CreateFile(fileName, content);
((EventHandler)delegate { templateProjectItem.ProjectItems.AddFromFile(fileName); }).BeginInvoke(null, null, null, null);
} //刪除文件
internal override void DeleteFile(String fileName) {
((EventHandler)delegate { FindAndDeleteFile(fileName); }).BeginInvoke(null, null, null, null);
} //根據(jù)文件名刪除文件
private void FindAndDeleteFile(String fileName) { foreach(EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems) { if (projectItem.get_FileNames(0) == fileName) {
projectItem.Delete(); return;
}
}
}
}#>
4、還是在類庫根目錄下新建 DbHelper.ttinclude 模板,主要是數(shù)據(jù)庫操作類
代碼太多就不放了
具體的代碼就不細(xì)講了,直接拷貝,或者下載我寫好的代碼即可。再次說明下,本文不會(huì)對(duì) T4 語法繼續(xù)深入研究。

5、實(shí)現(xiàn)生成全部實(shí)體類的模型設(shè)計(jì),在Blog.Core.FrameWork.Entity文件夾下,新建 Blog.Core.FrameWork.Entity.tt 模板

填寫代碼
//如果要獲取主機(jī)信息,記得把 hostspecific 設(shè)置成true
<#@ template debug="false" hostspecific="True" language="C#" #>
<#@ output extension=".cs" #>
//導(dǎo)入命名空間組件
<#@ assembly name="System.Data" #>
<#@ assembly name="System.xml" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ import namespace="System.Data" #>
<#@ assembly name="System.Core.dll" #>
<#@ assembly name="System.Data.DataSetExtensions.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
//引入我們的公共模板文件
<#@ include file="$(ProjectDir)DbHelper.ttinclude" #>
<#@ include file="$(ProjectDir)ModelAuto.ttinclude" #>
//定義我們的輸出文件夾
<# var OutputPath1 = Path.GetDirectoryName(Host.TemplateFile)+"\\work"; if (!Directory.Exists(OutputPath1))
{
Directory.CreateDirectory(OutputPath1);
} var manager = new Manager(Host, GenerationEnvironment, true) { OutputPath = OutputPath1 };
#>
//-------------------------------------------------------------------- // 此代碼由T4模板自動(dòng)生成 // 老張的哲學(xué) 生成時(shí)間 <#=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")#> // 注意更新后會(huì)改變相應(yīng)代碼。 //--------------------------------------------------------------------
<# var tableName=config.TableName;//獲取config配置中的表名,為單一生產(chǎn)使用
#>
<# if(tableName!=""){//如果表名有值,表示是生成單一文件
#>
//引用命名空間
using System; namespace Blog.Core.FrameWork.Entity
{ /// <summary>
/// <#=tableName#>
/// </summary>
public class <#=tableName#>//可以在這里加上基類等
{ //將全部字段遍歷出來
<# foreach(DbColumn column in DbHelper.GetDbColumns(config.ConnectionString, config.DbDatabase, config.TableName)){#>
public <#= column.CSharpType#><# if(column.CommonType.IsValueType && column.IsNullable){#>?<#}#> <#=column.ColumnName#> { get; set; } <#}#> }
} //如果為空,表示要將整個(gè)數(shù)據(jù)庫都生成出來
<#
} else{
#>
//連接數(shù)據(jù)庫,打開 connect 連接
<#
SqlConnection conn = new SqlConnection(config.ConnectionString);
conn.Open();
System.Data.DataTable schema = conn.GetSchema("TABLES");
#>
//遍歷全部數(shù)據(jù)庫表
<# foreach(System.Data.DataRow row in schema.Rows)
{ #>
//開始啟動(dòng)block塊,參數(shù)是實(shí)體類文件名
<#
manager.StartBlock(row["TABLE_NAME"]+".cs");
#>
//----------<#=row["TABLE_NAME"].ToString()#>開始----------
using System; namespace Blog.Core.FrameWork.Entity
{ /// <summary>
/// <#=tableName#>
/// </summary>
public class <#=row["TABLE_NAME"].ToString()#>//可以在這里加上基類等
{ //將該表下的字段都遍歷出來,可以自定義獲取數(shù)據(jù)描述等信息
<# foreach(DbColumn column in DbHelper.GetDbColumns(config.ConnectionString, config.DbDatabase, row["TABLE_NAME"].ToString() )){ #>
public <#= column.CSharpType#> <# if(column.CommonType.IsValueType && column.IsNullable){#> ?<#}#> <#=column.ColumnName #> { get; set; } <#}#> }
} //----------<#=row["TABLE_NAME"].ToString()#>結(jié)束----------
<#
manager.EndBlock();
}
manager.Process(true);
}
#>
這里我寫的比較凌亂,大概就是這個(gè)邏輯,其實(shí)和 C# 特別相像,主要簡單的看一看,就都會(huì)明白,這個(gè)時(shí)候我們保存文件 ctrl+S,如果你連接數(shù)據(jù)庫正確的話,會(huì)發(fā)現(xiàn)已經(jīng)生成了全部實(shí)體類文件。

隨便打開一個(gè)實(shí)體類,就可以發(fā)現(xiàn),和我們自己寫的是一樣的:是不是很方便!
//----------BlogArticle開始----------
using System; namespace Blog.Core.FrameWork.Entity
{ /// <summary>
/// BlogArticle /// </summary>
public class BlogArticle//可以在這里加上基類等
{ //將該表下的字段都遍歷出來,可以自定義獲取數(shù)據(jù)描述等信息
public int bID { get; set; } public string bsubmitter { get; set; } public string btitle { get; set; } public string bcategory { get; set; } public string bcontent { get; set; } public int btraffic { get; set; } public int bcommentNum { get; set; } public DateTime bUpdateTime { get; set; } public DateTime bCreateTime { get; set; } public string bRemark { get; set; }
}
} //----------BlogArticle結(jié)束----------
這個(gè)時(shí)候你會(huì)問,這個(gè)既然這么好,能全部生成其他層么?沒錯(cuò),答案當(dāng)然是肯定的!
3、實(shí)現(xiàn)整個(gè)系統(tǒng)框架模板
因?yàn)槠膯栴},這里就不把代碼粘貼出來了,大家自己去Github 上獲取即可,最終的效果是醬紫的:

是不是很神器!整個(gè)系統(tǒng)框架就在一瞬間就加載出來了,大家下載好后,只需要 ctrl + S 保存一下 tt 模板文件,就能全部生成,只需要把精力放到 Base 基類/基接口即可,以后數(shù)據(jù)庫表結(jié)構(gòu)無論如何變化都不怕!
四、SqlSugar等ORM框架來實(shí)現(xiàn) DbFirst
總結(jié)來說,其實(shí)一般的 ORM 框架,都是集成的 T4模板,EF是如此,咱們的SqlSugar 也是這樣,已經(jīng)封裝好了相應(yīng)的邏輯代碼:
using (SqlSugarClient db =SqlSugarInstance.GetInstance())
{ try { // db.CodeFirst.InitTables(typeof(實(shí)體類model));
db.DbFirst.Where("數(shù)據(jù)庫表名").CreateClassFile("D:\\Demo\\2.cs");//生成文件的·地址
} catch (Exception ex)
{
}
}
還記得咱們 Repisitory 層中的 DbContext.cs 文件么,里邊我已經(jīng)封裝好了,直接在 BaseRepository.cs 或者自己寫的任何文件中調(diào)用就行,只不過這里僅僅是 Model 層的。這里就不多說了。

五、結(jié)語
今天也是在忙碌中,抽出時(shí)間寫了寫,主要是給大家分享了下,如何快速的半自動(dòng)化的實(shí)現(xiàn)項(xiàng)目搭建,也算是一個(gè)小技巧了,重點(diǎn)說明了下 T4模板搭建全部層文件,個(gè)人感覺還是可行的,主要是靈活性很強(qiáng),需要的小伙伴可以好好的研究一下,繼續(xù)奮斗中!!!