當我們使用Visual Studio來新建某個項目(Project)時,通常都會使用File –> New –> Project菜單來打開New Project(新建項目)對話框,里面列出了各種項目類型以供我們選擇。大部分讀者朋友都應該知道,這個對話框其實是列出了所有已經安裝的項目模板,不僅如此,Visual Studio還允許用戶通過File –> Export Template菜單將現有的項目導出為項目模板。
平時我們最為常見的是使用Export Template來創建單一項目的項目模板,此時使用Export Template功能就十分有效。當然,社區里也有一些工具(比如微軟官方的Export Template Wizard工具)能夠幫忙創建項目模板,甚至可以直接將模板壓縮包文件編譯成供Visual Studio 2010使用的擴展安裝程序(VSIX),這樣用戶就可以通過VSIX直接將項目模板安裝到Visual Studio 2010中。雖然社區已經提供了各種各樣的創建項目模板的方法,而且網上有關項目模板創建的文章和論壇帖子也不少,但大多數都是在討論單一項目模板的創建。本文打算從一個案例解決方案開始,邊做邊討論,看看在Visual Studio 2010中是如何創建多項目(解決方案)模板的。
案例解決方案 – RainbowCMS
為了演示的需要,我隨手新建了一個非常簡單的面向DDD的案例解決方案:RainbowCMS。這個解決方案並沒有包含任何界面,也沒有提供Web Service的接口,而僅僅是實現了一個非常簡單的管理客戶信息的應用服務。這個解決方案主要包含如下四個項目:
- RainbowCMS.Application:此項目包含了RainbowCMS的應用服務,以及與之相關的數據傳輸對象
- RainbowCMS.Domain:此項目包含了RainbowCMS的領域模型、倉儲接口以及與DDD相關的一些接口。為了演示需要,我只在RainbowCMS中提供了非常簡單的“客戶-地址”模型
- RainbowCMS.Domain.Repositories:此項目實現了針對“客戶”聚合的倉儲,它本身應該屬於領域層的一個插件,所以最后實現的時候,是需要通過IoC注射到應用層的
- RainbowCMS.Infrastructure:此項目是整個解決方案的基礎結構層,包含了一些與技術相關的基礎結構,比如IoCFactory
這個解決方案所包含的項目簡單地將一個面向DDD的應用程序的各個部分組織在一起(展現層除外),它僅僅用於演示Visual Studio 2010中多項目解決方案模板的創建過程,因此請讀者朋友不要過分地糾結其DDD架構的實現過程。
現在,我們開始基於RainbowCMS創建多項目解決方案模板。在開始之前,請先確保你的機器裝有Visual Studio 2010 SDK。
多項目解決方案模板的創建
在安裝了Visual Studio 2010 SDK之后,我們就可以開始創建項目模板了。在Visual Studio 2010中選擇File –> New –>Project菜單,在彈出的New Project對話框的Installed Templates下,選擇Visual C#/Extensibility節點,可以看到類似如下的模板選項:
現在,讓我們開始使用C# Project Template項目來創建多項目解決方案模板。
新建模板項目
在上面的New Project對話框中,選擇C# Project Template,在Name文本框中輸入項目名稱:CMSProjectTemplate,然后單擊OK按鈕。此時將創建一個名為CMSProjectTemplate的解決方案:
在該解決方案中真正有意義的部分是CMSProjectTemplate.vstemplate文件,CMSProjectTemplate.ico則是今后用於顯示在New Project對話框中的圖標;而其它的所有文件都是用來表示一個完整的C#項目的相關文件。由於現在我們是在創建多項目解決方案的模板,所以其它的這部分文件(包括AssemblyInfo.cs、Class1.cs以及ProjectTemplate.csproj)都可以刪掉。於是,我們的CMSProjectTemplate解決方案就變成了如下的結構:
更改CMSProjectTemplate.vstemplate文件
既然我們已經在解決方案中刪掉了一部分內容,那么CMSProjectTemplate.vstemplate文件也要作相應的修改。首先,找到VSTemplate節點的Type屬性,將其改為ProjectGroup,這一點很重要,否則今后產生的項目將不會被添加到解決方案資源管理器中;其次,將TemplateContent下所有的內容刪掉,並添加一個ProjectCollection節點。更改以后的CMSProjectTemplate.vstemplate內容如下:
<?xml version="1.0" encoding="utf-8"?> <VSTemplate Version="3.0.0" Type="ProjectGroup" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005"> <TemplateData> <Name>CMSProjectTemplate</Name> <Description><No description available></Description> <Icon>CMSProjectTemplate.ico</Icon> <ProjectType>CSharp</ProjectType> <RequiredFrameworkVersion>2.0</RequiredFrameworkVersion> <SortOrder>1000</SortOrder> <TemplateID>36e8d4bf-48bc-42c9-91ab-e45348393288</TemplateID> <CreateNewFolder>true</CreateNewFolder> <DefaultName>CMSProjectTemplate</DefaultName> <ProvideDefaultName>true</ProvideDefaultName> </TemplateData> <TemplateContent> <ProjectCollection> </ProjectCollection> </TemplateContent> </VSTemplate>
導出項目模板並添加到CMSProjectTemplate解決方案
打開RainbowCMS解決方案,然后選擇File –> Export Template菜單,此時將打開Export Template Wizard對話框,在對話框中選擇Project Template,表示我們要將所選的項目導出為項目模板,然后在下部的下拉框中,選擇需要導出為模板的項目。在這里我們選擇RainbowCMS.Application,然后單擊Next按鈕。
在Select Template Options頁中,設置與模板有關的參數,比如模板名稱和描述等,在此我們都使用默認參數,注意將Automatically import the template into Visual Studio復選框去掉,表示不需要將產生的模板直接應用到Visual Studio中。在單擊Finish后,向導會直接將導出模板所在的目錄以文件管理器的方式打開。
將產生的RainbowCMS.Application.zip文件解壓到當前文件夾備用。
回到CMSProjectTemplate項目,在項目中創建一個CMSTemplate的目錄,並在該目錄下創建Application目錄,通過復制/粘貼的方式將RainbowCMS.Application.zip中解壓出來的文件添加到CMSProjectTemplate項目的CMSTemplate/Application目錄下,之后按以下步驟操作:
- 將所有*.cs文件的編譯方式改為None,否則將出現編譯錯誤
- 將RainbowCMS.Application.csproj重命名為Application.csproj,並在MyTemplate.vstemplate中,將第一個Project節點的File屬性改為Application.csproj,TargetFileName屬性改為$safeprojectname$.Application.csproj。注意:這里的$safeprojectname$是項目模板的一個內置的宏,表示一個“安全”的項目名稱(也就是由Visual Studio處理過的,用戶在New Project對話框中輸入的那個項目名稱)。此外,對於單一項目模板而言,直接使用這個$safeprojectname$是沒有問題的,但如果是多項目解決方案的模板,那么要在各個項目中使用這個宏,就需要一些額外操作,這部分內容會在后面介紹。有關項目模板所使用的宏,請參見:模板參數
- 將CMSTemplate\Application目錄下Application.csproj以及所有C#文件中的RainbowCMS改為$safeprojectname$宏
- 修改CMSProjectTemplate.vstemplate文件,在ProjectCollection節點中添加如下代碼:
<ProjectTemplateLink ProjectName="$safeprojectname$.Application"> CMSTemplate\Application\MyTemplate.vstemplate </ProjectTemplateLink>
- 在Application.csproj文件中修改RootNamespace和AssemblyName,將其改為$safeprojectname$.Application
- 經過上面的修改,CMSProjectTemplate的解決方案內容如下:
用以上相同的步驟,將RainbowCMS.Domain、RainbowCMS.Domain.Repositories以及RainbowCMS.Infrastructure項目導出成模板並添加到CMSProjectTemplate解決方案。在完成了這一系列操作之后,CMSProjectTemplate解決方案如下所示:
至此,我們已經將所需要的項目模板加入到了CMSProjectTemplate中,直接編譯整個解決方案,就會在輸出目錄中出現一個ZIP文件,它就是Visual Studio的項目模板文件。將這個ZIP文件復制到<User_Documents>\Visual Studio 2010\Templates\ProjectTemplates\Visual C#目錄下,然后在Visual Studio中使用File –> New –> Project菜單打開New Project對話框,我們就可以在Visual C#的類別下找到CMSProjectTemplate的項目模板:
選中這個模板,然后為我們新建的項目起個名字,比如CMSTest1,然后單擊OK按鈕,我們可以看到,一個新的解決方案被創建了,它有着下面的結構:
與我們本文開始的RainbowCMS解決方案相比,除了項目名字不同(此時是CMSTest1)以外,其它的結構完全相同。雙擊IoCFactory.cs文件將其打開,我們發現,IoCFactory類所在的命名空間有誤,我們希望的是CMSTest1.Infrastructure,而產生的代碼里卻是CMSTest1.Infrastructure.Infrastructure:
namespace CMSTest1.Infrastructure.Infrastructure { public static class IoCFactory { public static T GetObject<T>() { // TODO: Implement the IoC/DI logic here. return default(T); } } }
打開CMSProjectTemplate項目下的CMSTemplate\Infrastructure.IoCFactory.cs文件,內容如下:
namespace $safeprojectname$.Infrastructure { public static class IoCFactory { public static T GetObject<T>() { // TODO: Implement the IoC/DI logic here. return default(T); } } }
看來在這里$safeprojectname$指代的是CMSTest1.Infrastructure,而不是CMSTest1。通常,我們希望$safeprojectname$在解決方案的各個項目中,都是指代用戶輸入的項目名稱(即CMSTest1),而不是每個項目各取不同的值。這樣做其實很重要:比如在指定一個項目對另一個項目的引用時,例如:CMSTest1.Domain如果需要引用CMSTest1.Infrastructure,那么就需要在Domain.csproj中的項目引用部分加入$safeprojectname$.Infrastructure,然而如果$safeprojectname$指代的是CMSTest1.Domain的話,你就無法在Domain.csproj中加入這一引用,因為它會被替換成CMSTest1.Domain.Infrastructure。要解決這個問題,我們需要使用Template Wizard。我將在下一篇文章中詳細介紹Template Wizard的使用。
本文案例下載
- RainbowCMS解決方案
- CMSProjectTemplate解決方案(至目前為止,未完成)