在作項目的時候,常常須要根據表結構create一些實體類,寫多了,實在是以爲無趣,因而就琢磨着作個代碼生成工具。固然如今有不少現成的,拿來用就好,但是總想本身弄個出來玩玩,一來是當初用DataSet,VS能夠根據一個xsd文件生成那麼多代碼,能夠拖拖拽拽就搞定,一直沒鬧明白是怎麼作的,不甘心,總想弄明白,二來,公司裏,數據庫的腳本大可能是根據一個xml配置文件生成的,這樣,我拿到這個xml生成代碼也是挺方便的。html
有了這個想法,可徹底沒頭緒該怎麼開發VS add-in, 無奈,只能仰仗Google大神了,「VS 插件」, 「自動代碼生成 vs site:cnblogs.com」 … 等等,一頓狂搜,瞭解到了相關的技術有:VS Add-in, CodeDom, T4, Template, Visual Designer, Domain-Specific Languages, Custom tools, etc.數據庫
VS Add-in: 可使用任何.Net語言開發VS的插件,能夠操做開發環境中的任何元素:解決方案,項目,文件,菜單,Solution Explorer, Output window…數據結構
CodeDom: 代碼文檔對象模型。相似Xml的Dom操做,CodeDom將代碼轉換成一種數據結構,經過操做這種數據結構能夠實現生成代碼,修改代碼,動態編譯等等各類功能。app
T4: Text Templating Transformation Toolkit, 經過模板加代碼的方式產生代碼,能夠想象下aspx頁面,裏面有html代碼,也有.Net代碼,經過轉換,最後呈現出html頁面。函數
Template: 模板,在添加新項的時候,在對話框中的那些就是模板。工具
Visual Designer: 像類型化DataSet,它有一個可視化的編輯界面,經過拖拽,點幾下鼠標等等操做就能夠設計出咱們想要的DataSet類型。學習
Domain-Specific Languages: 特定領域模型語言,經過它,咱們能夠設計出Visual Designer。this
Custom Tools: 自定義工具,經過一個文件生成另外一個文件。spa
CodeDom太複雜,用來生成代碼若是殺雞用牛刀,Visual Designer, Domain-Specific Languages,嗯,也用不到,暫時我不須要作個可視化的界面去設計個人代碼,等我哪天要實現個相似類型化DataSet那樣的插件的時候再考慮吧。去掉那些複雜的東西,我如今要用到的技術有:VS Add-in,用來實現VS的插件,使代碼生成工具集成進VS;T4, 用來產生代碼; Template,定義一個模板。插件
先說T4,看個簡單的列子:
首先建立一個項目,而後添加新項,選擇文本模板
文件裏包含以下代碼
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".txt" #>
在文本模板中,<#@ #>中是指令,定義了模板的一些行爲,如第二行,output extension=」.txt」,意思是說,該模板最後轉換生成的文件的後綴是.txt的。固然,我須要的代碼文件,因此將其改成.cs便可。將文本修改爲下面這個樣子:
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
using System;namespace T4
{
public class T4Test
{
}
}
保存後,能夠看到Solution Explorer裏面多了一個.cs文件
打開cs文件,能夠看到裏面包含以下代碼,跟我在模板文件裏的內容同樣。
using System;
namespace T4
{
public class T4Test
{
}
}
關於T4的語法,cnBlogs裏有太多解釋,這裏就不更進一步的描述了。
但是,這樣一個模板文件產生的代碼是固定的,而我須要的是輸入一段xml,根據xml來生成相應的代碼文件。要使模板文件知道xml裏的內容,則須要將其內容做爲參數傳遞到模板裏,這個時候就須要用到參數指令:
<#@ parameter type="" name="" #>
而後再將以前的模板修改成:
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<#@ parameter type="System.String" name="className" #>
using System;namespace T4
{
public class <#= className #>
{
}
}
若是這個時候,點擊保存,VS會提示你轉換失敗,由於輸入的參數爲空。這個時候,須要修改模板文件的屬性,在自定義工具裏,咱們能夠看到顯示的是TextTemplatingFileGenerator,它負責將模板文件轉換成目標文件。可是在這裏,我須要的是將模板轉換成代碼生成類,將該屬性修改成:TextTemplatingFilePreprocessor。這個時候再保存,就會發現原來的cs文件裏的內容變成了一段長的代碼:
namespace Test
{
using System;
#line 1 "E:\My Files\Documents\Visual Studio 2010\Projects\MStarGATDataEntityGenerator\NUTest.GAT.Common\TextTemplate1.tt"
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "10.0.0.0")]
public partial class TextTemplate1 : TextTemplate1Base
{… …
記得原來用VS2003的時候,尚未T4技術,那個時候也寫了一個代碼生成工具,那個幸苦啊,一行行的WriteLine,又要控制輸出代碼的縮進,長長的一大段代碼。如今TextTemplatingFilePreprocessor工具將模板自動的生成了那個類,工做強度上輕鬆多了。在這個TextTemplate1類裏有個TransformText的方法,調用它,就會根據模板輸出代碼。不過如今還不能直接去調用,不然仍是會出現跟使用TextTemplatingFileGenerator工具時同樣的錯誤。由於,咱們仍是沒有設置className變量的值。但是找遍整個TextTemplate1類,既沒有在構造函數裏發現參數能夠設置,也沒發現公共屬性,也沒方法,只有一個私有的_classNameField變量。好在生成的TextTemplate1類是partial的,咱們能夠本身定義一個屬性去設置className,而後再調用TransformText方法。好比設置className爲「Table」,咱們將獲得代碼:
using System;
namespace T4
{
public class Table
{
}
}
好了,到此,關於T4要用到的知識就這些了。
下面就是VS Add-in了。前面說了,我想當在VS裏添加了一個新文件,而後將一段xml複製到文件裏,而後保存,最後就能獲得代碼文件。前面T4已經能夠幫助我實現生成代碼文件,如今我須要的是可讓我在保存文件的時候將文件的內容傳遞給TextTemplate1類,而後調用TransformText方法,而後將獲得的代碼內容保存到一個cs文件中,並添加到項目裏。而這些工做就得依靠VS Add-in來幫我實現了。
首先,咱們須要添加一個VS Add-in的項目
找到Connect.cs文件,在OnConnection方法中,添加如下代碼
DocumentEvents docMasterEvents = this.application.Events.get_DocumentEvents(null);
docMasterEvents.DocumentSaved += new _dispDocumentEvents_DocumentSavedEventHandler(
docMasterEvents_DocumentSaved);
以及方法
void docMasterEvents_DocumentSaved(Document Document)
{
if (Document == null) return;
if(!Document.Name.ToLower().EndsWith(「.xml」)) return;TextTemplate1 gen = new TextTemplate1();
gen.className = 「Table」;
string code = gen.TransformText();
// todo: 將代碼寫入到文件裏,並添加到項目中
}
在上面的DocumentSaved方法裏,若是發現文件後綴是xml的,就去調用模板。這裏有個問題,那就是,會對全部的xml都去調用。但是在一個項目裏總會有些其餘的xml文件,這些文件是不須要轉換的。固然能夠對文件內容進行判斷,再決定要不要調用模板,不過更簡單的方法是用一個特殊的文件名,而不是用.xml。好比在個人項目裏,我使用.msgatde做爲文件名後綴。講到這裏,又該介紹下模板了,這裏的模板是指添加新項時,那些可選擇的項目。
爲何要講模板呢,原本實現這個代碼自動生成的工具就是圖個方便,可若是每次都要本身將新建的xml文件的後綴改爲我本身定義的那個後綴,多麻煩,說不定還很容易寫錯。全部爲了更一步簡化操做,我打算定義一個模板,這樣在添加新項的時候,就自動生成一個.msgatde後綴的文件。
一個模板裏,通常會至少包含3個文件:
第一個,圖標文件,在對話框裏顯示用的。
第二個,模板文件,添加新項後,VS會根據這個文件產生新的文件添加到項目中。
第三個,模板描述文件。
如今來看下第三個文件的內容:
其中ProjectItem那一段即定義了模板的行爲:經過模板裏的DataEntity.msgatde文件產生目標文件$fileinputname$.msgatde。
在這些文件編輯好後,須要將其打包成一個zip文件,並將其放置到$my documents$\Visual Studio 2010\Templates\ItemTemplates\Visual C#目錄下。如今在選擇添加新項,便可看到咱們定義的模板了。
ok,講完了怎麼定義一個模板,如今該接着講VS Add-in了。在前面的DocumentSaved方法裏尚未實現如何將生成的代碼添加到項目裏,如今接着說。
首先,須要將獲得的代碼保存到文件裏,這裏就有一個問題了,如何知道路徑呢?在DocumentSaved方法中,有一個Document的參數,這個就是表示了當前的文檔,Document有個屬性ProjectItem,經過該屬性能夠獲取到當前文件的路徑:
string fullname = Document.ProjectItem.Properties.Item(「FullPath」).Value.ToString();
知道了路徑,就容易多啦:
string codeFile = fullname.Repleace(「.msgatde」, 「.cs」);
using(StreamWriter sw = new StreamWriter(codeFile))
{
sw.Write(codeString);
}
如今文件是有了,該把它加到項目裏了,很簡單:
Document.ProjectItem.ProjectItems.AddFromFile(codeFile);
說到這個方法,要順便說一句。都知道當咱們添加了一個.aspx文件後,會自動產生一個.aspx.cs的文件,而且這個文件會存在於.aspx文件下,這是個很酷的特性,以前在搜索如何實現的時候,網上給出了大體有3種方法,如修改preject file的內容,添加dependupon元素,或者修改註冊表等方法,其實不用那麼麻煩,經過上面的方法,.cs文件自動被添加到.msgatde文件下面了,效果以下:
最後講下部署插件,在插件的項目裏,有個{prjoectname}.AddIn文件,將其copy到\Documents\Visual Studio 2010\Addins目錄下,而後將其用文本編輯軟件打開:
其中第十行是說明該插件的dll文件所在的路徑,將其修改成正確的路徑。固然,你也能夠將文件也複製到該目錄下。
ok,目前關於如何建立一個代碼生成插件的部分就講完了。這裏只是記錄了我在學習如何建立這個插件過程當中遇到的難點,不少細節部分並無寫下來,還有不少東西須要完善,好比每次生成代碼,能夠把生成的結果輸出到output window,若是出錯了,也把錯誤的緣由output出來。
private void OutputToOPW(string format, params object[] objs)
{
OutputWindow ow = this.applicationObject.ToolWindows.OutputWindow; // applicationObject 是 一個DTE2對象
OutputWindowPane owp = null;
try
{
owp = ow.OutputWindowPanes.Item("MyAddin");
}
catch
{
owp = ow.OutputWindowPanes.Add("MyAddin");
}
owp.Activate();
owp.OutputString(string.Format(format, objs));
}
最後,推薦一本書:《Practical.Code.Generation.in.NET》。多虧了這本書,才解決了我以前的不少困擾。
原文:http://www.cnblogs.com/FMax/archive/2011/07/09/2101699.html