很多時候會想在vs編譯的時候自訂一些事情或者動作,
例如:
拷貝產生的檔案到特定的目錄。
部署程式到測試目錄或者環境,例如註冊到windows服務,更新GAC等。
根據編譯環境產生特定的設定檔(例如web.config) PS: 身在一個複雜環境, 這是我最想要的功能。
自動執行外部exe。
同步DLL和其他資源檔。
1.最簡單的自然是用Visual Studio內建的編譯事件,這東西使用方便,又是Visual Studio內建的功能,就是功能弱了一點(好吧 其實是很弱)
將項目產生的DLL檔案拷貝到特定目錄,(如果你想拷貝一整個檔案夾 用xcopy; 當然,熟悉命令列的人可以弄出更多的玩法)
如所示
2.另外一種比較推薦的方式是自訂編譯擴充(可以執行C#代碼...功能強大多了), 看下面這個專案檔的最後幾句 (專案檔就是 項目名.csproj)
<Import Project="..\..\Build\Tasks\Build.tasks" />
<Target Name="BeforeBuild">
<Message Text="Start Automatic Generate Configuration File, Source File: $(ProjectDir)web.config" Importance="high">
</Message>
<ConfigurationEnvironmentTask TemplateFile="$(ProjectDir)web.template.config" TargetFile="$(ProjectDir)web.config" DataSource="$(EnvironmentName).config" />
</Target>
</Project>
這幾句話的意思是
1. 包含一個task檔案 (該檔案包含了ConfigurationEnvironmentTask 的定義,這是一個自定開發的類,其主要作用是根據環境產生web.config檔案)
2.輸出一行提示資訊 Start Automatic..... (該資訊將顯示在Output Window)
3. 調用ConfigurationEnvironmentTask 並傳入一些參數(TemplateFile 等都是自己定義的參數)
Build.tasks的檔案內容其實很簡單,主要就是說明了這個task定義在哪裡 (Build.dll)
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask AssemblyFile="Build.dll" TaskName="Build.ConfigurationEnvironmentTask"/>
</Project>
接下來的重點就是如何?這個Task了,以下是Build.dll的關鍵代碼
View Code
namespace Build
{
#region namespace
using System.Globalization;
using System.IO;
using System.Xml;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
#endregion
#region Standard Config File Sections
/// <summary>
/// this class generate config / xml file by template
/// </summary>
public class ConfigurationEnvironmentTask : Task
{
#region Public Propreties
/// <summary>
/// Gets or Sets template file path (full path : C:\web.config)
/// </summary>
[Required]
public ITaskItem TemplateFile { get; set; }
/// <summary>
/// Gets or Sets traget file path, will automatic overwrite this file (full path : C:\web.config)
/// </summary>
[Required]
public ITaskItem TargetFile { get; set; }
/// <summary>
/// Gets or Sets configuration file, will get settings from this path (full path : C:\web.config , relative path: dev.config)
/// </summary>
[Required]
public ITaskItem DataSource { get; set; }
#endregion
/// <summary>
/// execute replace logic
/// </summary>
/// <returns>ture successful, false failure</returns>
public override bool Execute()
{
string templateContent = File.ReadAllText(this.TemplateFile.ToString());
XmlDocument xmldoc = new XmlDocument();
string fileName = null;
string fileFolder = null;
if (Path.IsPathRooted(this.DataSource.ToString()))
{
fileFolder = Path.GetDirectoryName(this.DataSource.ToString());
}
else
{
fileFolder = Path.GetDirectoryName(this.TargetFile.ToString());
}
fileName = Path.GetFileName(this.DataSource.ToString());
if (fileName == null || string.IsNullOrEmpty(fileName) || fileName.ToLower(CultureInfo.CurrentCulture).Trim() == ".config")
{
fileName = @"local.config";
}
this.Log.LogMessage("DataSource:" + this.DataSource.ToString());
this.Log.LogMessage("IsPathRooted:" + Path.IsPathRooted(this.DataSource.ToString()));
this.Log.LogMessage("fileFolder:" + fileFolder);
this.Log.LogMessage("fileName:" + fileName);
if (!File.Exists(fileFolder + @"\" + fileName))
{
throw new FileNotFoundException("Can not find File :" + fileFolder + @"\" + fileName);
}
xmldoc.Load(fileFolder + @"\" + fileName);
foreach (XmlNode node in xmldoc.SelectNodes("/Settings/*"))
{
templateContent = templateContent.Replace("$(" + node.Name + ")", node.InnerText).Trim(new char[] { ' ', '\r', '\n', '\t' });
}
File.WriteAllText(this.TargetFile.ToString(), templateContent);
return true;
}
}
#endregion Standard Config File Sections
}
代碼寫得很爛,這不是生產環境的code
關鍵就是Execute這個方法,
大意就是根據指定的config檔案讀取出索引值對,將web.config中類似於 $(key)的內容替換掉
PS:在正式環境中,其實我們是用放在一個網站上的一個檔案和環境變數實現的
當然在這個方法中你想幹嘛幹嘛,修改設定檔也好,發郵件也好,自動備份也好...
對了 記得讓你的項目引用Build.dll (推薦dll和task檔案放在同一個目錄)
然後點擊編譯或者Ctrl+Shift+B吧
本文中的所有代碼在Visual Studio 2010和 MSBuild Extenstion 4.0 編譯和運行成功
此外MS Build Exenstion中的一些功能很不錯,而且網上也有很多第三方的Task都挺不錯的, 例如部署IIS網站,FTP等,有興趣可以試試,也許能簡化不少工作