Microsoft 將 Team Foundation Server (TFS) 構建為一個主要和次要服務的集合,包括版本控制、 工作項目跟蹤和 EventService 服務。我將 EventService 歸類為次要服務,或者更確切地說是支持服務 。EventService 提供了一組事件,觸發後,這些事件可執行一些操作(如發送電子郵件或調用基於 SOAP 的 Web 服務)。
在本專欄中,我將介紹 Visual Studio® 用戶界面提供了哪些現成的事件、EventService 提供了 哪些事件、如何創建和管理訂閱以及如何創建自己的 Web 服務來接收和處理事件。盡管我將使用 Visual Studio 2008 和 TFS 2008,但本專欄的大部分內容同樣適用於 TFS 2005。
項目警報
如果 啟動安裝有 Team Foundation Client (TFC) 的 Visual Studio 2008,並連接到現有的團隊項目,您將 可以訪問主菜單欄上的“團隊”菜單。此時,其中有個名為“項目警報”的菜單項 。如果選擇此菜單項,Visual Studio 將打開“項目警報”對話框,可在其中為最多四個事件 創建電子郵件訂閱(請參閱圖 1)。TFS 將把電子郵件正文轉為純文本或 HTML 格式,具體取決於您的偏 好。
圖 1 TFC 的項目警報
Visual Studio 提供了以下四個事件:項目簽入、生成完成、生成狀態更改以及其他人更改了分配給 您的工作項目。對於每種事件,可輸入一個或多個電子郵件地址(以分號分隔)。可一個事件一個事件地 選擇電子郵件消息的格式(HTML 或純文本)。請注意,TFS 可引發的事件多於“項目警報” 對話框中所列出的事件。(稍後將詳細介紹。)
控制電子郵件消息格式
TFS 通過一組 XSL 轉換來控制警報電子郵件消息的格式。在文件夾 C:\Program Files\Microsoft Visual Studio 2008 Team Foundation Server\Web Services\Services\v1.0\Transforms 中,共有 28 個文件。這些文件分 為以下四類:eMailTemplate、plantextXsl、XSD 和 XSL。Microsoft 使用 XSD 文件來定義所提供事件 的數據布局。事件服務使用 XSL 和 plaintextXsl 文件將 XML 數據轉換到電子郵件消息正文中。 Microsoft 為“項目警報”對話框中顯示的四個事件創建了 XSL 文件。請注意,在 TFS 2008 中,Microsoft 增強了“生成完成”事件,並因而創建了後面帶有數字 2 的新 XSL 文件。
最後,eMailTemplate 文件引用 EventService 提供的另外 5 種事件。但是,Microsoft 不再使用或 支持這些文件。代之以使用 DataChangedEvent.xsl 文件。“項目警報”對話框並未提供這些 事件,因此要訂閱它們並接收電子郵件通知,您需要使用 Microsoft 提供的一個命令行工具 (BisSubscribe.exe) 或自己編寫一些代碼。
要控制通知電子郵件消息的格式,需編輯適當的轉換 文件。對於通過“項目警報”對話框提供的事件,需要編輯一個或多個 XSL 文件。對於 HTML 格式的電子郵件消息,您會發現所有事件的核心布局都存儲在 TeamFoundation.xsl 中。應首先針對要更 改的所有文件創建一個備份副本,然後再測試更改。完成更改後,TFS 將在下次引發事件時使用修改後的 轉換。
可用事件
在設計 EventService 服務時,Microsoft 非常注重靈活性和可擴展性。 可使用多種技術來指出所提供事件的列表。我已介紹了第一種方式:只需查看 Transforms 文件夾中存儲 的 XSD 文件。
可通過編程方式訪問“注冊服務”,這項支持服務可獲取服務列表以及 提供的事件。圖 2 顯示了需要用到的代碼,並提供了輸出示例。
Figure 2 注冊服務定義的事件
代碼
If mtfs IsNot Nothing Then Dim rs As IRegistration = _ DirectCast(mtfs.GetService(GetType(IRegistration)), IRegistration) Dim res() As RegistrationEntry = _ rs.GetRegistrationEntries(String.Empty) Debug.WriteLine("Registration Entries and their Event Types.") For Each re As RegistrationEntry In res Debug.WriteLine(re.Type) If re.EventTypes.Length > 0 Then For Each et As EventType In re.EventTypes Debug.WriteLine("-->" & et.Name) Next Else Debug.WriteLine("-->No Events Found") End If Next End If
輸出
Registration Entries and their Event Types. Build -->Build Completion Event -->Build Completion Event 2 -->Build Status Changed Event vstfs -->BranchMovedEvent -->NodeCreatedEvent -->NodePropertiesChangedEvent -->NodeRenamedEvent -->NodesDeletedEvent -->ProjectCreatedEvent -->ProjectDeletedEvent Reports -->No Events Found Wss -->No Events Found WorkItemTracking -->No Events Found VersionControl -->CheckinEvent TestTools -->No Events Found
請注意,WorkItemTracking 系統不會提供事件。但是,如果查看 Transforms 文件夾,您會發現其中實際上存在一個 WorkItemChangedEvent.xsd 文件。此外,在 Visual Studio 2005 和 Visual Studio 2008 之間,Microsoft 修改了引發與組安全性服務 (GSS)、授權和公共 結構服務 (CSS) 相關的特定事件的方式。圖 3 顯示了 EventService 當前所提供的事件列表。應將未在 圖 3 中列出的事件視為已被淘汰。
Figure 3 服務和支持的事件
服務 事件 是否列在注冊服務中? 工作項跟蹤 WorkItemChangedEvent 否 版本控制 CheckInEvent 是 團隊項目生成 BuildCompletionEvent2 是 團隊項目生成 BuildStatusChangeEvent 是 團隊項目生成 BuildCompletionEvent 是 公共結構服務 ProjectCreatedEvent 是 公共結構服務 ProjectDeletedEvent 是 GSS/授 權/CSS DataChangedEvent 否在 DataChangedEvent.xsd 中定 義的新 DataChangedEvent 取代了圖 2 中列出的 vstfs 服務所提供的事件。當 TFS 觸發 DataChangedEvent 時,您會發現它將成為以下三種子類型之一:結構、安全性或標識。然後,需要調用 適當的 Web 服務來獲取與這些事件相關的詳細信息。TFS 2008 將針對為生成完成創建的所有新訂閱觸發 BuildCompletionEvent2 事件。如果已升級服務器,TFS 將繼續為升級時存在的所有訂閱觸發 BuildCompleteEvent。
創建訂閱
如果希望為 Visual Studio“項目警報”對話 框中未提供的事件創建訂閱,有兩種方法。第一種方法是使用隨 TFS 安裝在 C:\Program Files\Microsoft Visual Studio 2008 Team Foundation Server\TF Setup 處的 BisSubscribe.exe 命 令行工具。請注意,此工具還隨同 Visual Studio Team Server (VSTS ) SDK(它也是更大型 Visual Studio SDK 的組成部分)一並提供。但是,SDK 中的版本已過時,因此應確認當前使用的版本與 TFS 安 裝隨附的版本相同。
BisSubscribe.exe 支持以下兩個命令:subscribe 和 unsubscribe。Subscribe 是隱式命令。它支持 subscribe 最多使用六個參數,unsubscribe 使用兩個參數。圖 4 列出了該工具的 help 命令所提供的 參數。
Figure 4 BisSubscribe.exe 的命令行參數
參數 命令 注釋 eventType Subscribe 事件名稱。區分大小寫。 filter Subscribe 篩選器表達式。默認值為 none。 address Subscribe 訂閱者的電子郵件地址或 Web 方法 URL。 server Subscribe/Unsubscribe Team Foundation Server 的名稱 。 tag Subscribe 用於稍後標識該訂閱的一個字段。默認值為 none。 deliveryType Subscribe 指出首選的消息傳遞類型: EmailHtml、EmailPlaintext 或 SOAP。默認值為 SOAP。 id Unsubscribe 取消訂閱時要刪除訂閱的整數型 ID。例如,如果要為整個服務器訂閱簽入事件,可發出以下命令(全部在同一行中) :
bissubscribe /eventType CheckinEvent /address [email protected] /deliveryType EmailHtml /server tfsrtm08
在執行此命令時,BisSubscribe 將返回類似以下的一條消息:
TF50001: Created or found an existing subscription. The subscription ID is 7.
可使用該訂閱 ID 來取消訂閱:
bissubscribe unsubscribe /id 7 /server tfsrtm08
請注意,如果不打算編寫代碼,則需要手動跟蹤 subscribe 命令所返回的 ID。無法使用命令行獲取訂閱列表。盡管可在 SQL Server® 2005 數據庫 中查找,但既不推薦也不支持使用此方法。
當然,可能並不希望針對每個事件發送電子郵件。因 此,可添加一些篩選功能:
bissubscribe /eventType CheckinEvent /address [email protected] /deliveryType EmailHtml /server tfsrtm08 /filter "'Artifacts/Artifact[@TeamProject = MSFAgile]' <> null"
此篩選器用於告訴 TFS:您只想在 MSFAgile 團隊項目中發生簽入時才收到電子郵 件通知。
盡管 BisSubscribe 有效,但它並非最直觀的工具。而且,它也無法獲取現有訂閱列表 。因此,第二種方法(即編寫一些自己的代碼)似乎更有前途。
我創建了一個可供您下載的簡單 應用程序。它建立在之前幾期專欄中工作項目代碼(它們提供了用於連接到 TFS 2008 安裝的 UI 和框架 )的基礎之上。要獲取用戶帳戶的訂閱列表,需使用 TeamFoundationServer 對象的 GetService 方法連 接到 EventService,並檢索 IEventService 引用。IEventService 提供了一個 EventSubscriptions 方 法,此方法接受“用戶域名和用戶 ID”格式的用戶 ID,並返回全零數組或多個 Subscription 實例:
Dim es As IEventService = _ DirectCast(mtfs.GetService( _ GetType(IEventService)), IEventService) Dim userSubs As Subscription() = _ es.EventSubscriptions( _ Environment.UserDomainName & _ "\" & Environment.UserName) For Each usersub As Subscription In userSubs ' Do something Next
Subscription 對象提供了多個屬性,包括取消用戶對某個事件的訂閱所需的極端關鍵 ID。通過使用 ID 屬性,只需針對有效的 IEventService 引用執行 UnsubscribeEvent 方法即可,無需再執行其他操作 。
創建新訂閱同樣非常簡單,但需要特別注意一點。IEventService 提供了一個 SubscribeEvent 方法。就像 BisSubscribe.exe 一樣,它接受多個參數。難點在於需為定義篩選表達式這一操作創造良好 的用戶體驗,並且還必須編寫代碼來確保表達式的正確性。在 CodePlex 站點 (codeplex.com/tfseventsubscription) 上提供有一個社區項目,其中包括用於定義事件篩選器的示例代 碼。
創建自定義事件偵聽器
盡管電子郵件通知非常有用,但您可能還希望執行一些代碼來響應特定 事件。EventService 服務支持基於 SOAP 的通知,因而允許使用喜歡的任意代碼類型來捕獲和處理事件 。一個簡單的入門方法是查看現有代碼。
針對 Team Foundation Server 2005,Microsoft 發布 了一個示例來展示可如何使用 EventService 和一些代碼實現持續集成。在 msdn2.microsoft.com/ms364045中提供了相關文章以及代碼下載。對於 TFS 2008,已不再需要該示例( 並且如果不做修改,它將無法正常運行)。但是,它展示了可如何使用少量 C# 來創建基於 ASMX 的 Web 服務。在 Microsoft 示例的啟發下,我創建了一個可捕獲 WorkItemChanged 事件的簡單 Web 服務。
第一步需要創建一個 Web 服務項目。為測試該代碼,我使用了 Microsoft 提供的虛擬 PC 映像 — 必須首先在這種環境中測試您的代碼,否則可能將生產服務器搞得一團糟。您可以從 go.microsoft.com/fwlink/?LinkId=114644 下載該映像。
您可能希望使用 Visual Basic® 的 ASP.NET Web 服務應用程序模板。將名稱設為 MSDNMagTFSEvents 並將其放在 C:\Program Files\Microsoft Visual Studio 2008 Team Foundation Server\Web Services\ 中。此外,確保取消選 中“創建解決方案的目錄”選項。
Visual Studio 創建完項目後,調整項目屬性使項目 URL 使用端口 8080(通過“項目設計器 ”中的 Web 選項卡完成此操作)。然後,使用 IIS 管理工具在 Team Foundation Server Web 下 為您的項目創建一個 IIS 應用程序。請務必將 IIS 應用程序放在 Microsoft® Team Foundation Server 應用程序池中。完成後,嘗試調試服務以確保配置正確。
現在,將 Service1.asmx 重命 名為 ProcessEvents.asmx。將支持類的名稱從 Service1 改為 ProcessEvents。最後,如果願意,可將 Web 服務命名空間更新為適當的名稱。使用圖 5 中所示的原型替換現有的 HelloWorld WebMethod。
Figure5ProcessEvents WebMethod
<SoapDocumentMethod( _ "http://schemas.microsoft.com/TeamFoundation/2005" & _ "/06/Services/Notification/03/Notify", _ RequestNamespace:="http://schemas.microsoft.com" & _ "/TeamFoundation/2005/06/Services/Notification/03")> _ <WebMethod()> _ Public Sub Notify(ByVal eventXml As String, _ ByVal tfsIdentityXml As String) End Sub
第一個參數代表包含事件信息的 XML 文檔。可將其視作與常規 Microsoft .NET Framework 事件中的 EventArgs 等同的參數。第二個參數標識引發事件的 TFS 服務器。
此時, 已做好所有准備。當 TFS 引發事件時,EventService 將調用 Web 服務。在該方法中實現哪些功能完全 取決於您和您的創造力。圖 6 提供了將事件寫入 Windows® 事件日志的一個簡單示例。這段代碼首 先檢查確認 eventXml 參數包含數據。如果沒有數據,則會退出。假設存在需要分析的數據,代碼將使用 LINQ to XML 來查看已更改的字段集合是否包括 Title。如果包括,代碼將獲取舊值和新值以及工作項目 的 ID,並輸出該數據。
Figure 6 寫入事件日志
<SoapDocumentMethod( _ "http://schemas.microsoft.com/TeamFoundation/2005/06" & _ "/Services/Notification/03/Notify", _ RequestNamespace:="http://schemas.microsoft.com/TeamFoundation/" & _ "2005/06/Services/Notification/03")> _ <WebMethod()> _ Public Sub Notify(ByVal eventXml As String, _ ByVal tfsIdentityXml As String) Const logSource As String = "MSDNMagTFSEvents" Const logApp As String = "ProcessEvents" Dim el As New EventLog(logApp) el.Source = logSource Try el.WriteEntry("Notify event called.") If eventXml Is Nothing OrElse eventXml = String.Empty Then el.WriteEntry("eventXml data is empty.") Return Else Dim x As XElement = XElement.Load(New StringReader(eventXml)) Dim referenceName = (From element _ In x...<ChangedFields>...<ReferenceName> _ Where element.Value = "System.Title").SingleOrDefault If referenceName IsNot Nothing Then Dim oldValue = referenceName.Parent.<OldValue>.Value Dim newvalue = referenceName.Parent.<NewValue>.Value Dim refNameId = (From element _ In x...<CoreFields>...<ReferenceName> _ Where element.Value = "System.Id").SingleOrDefault ' the Work Item's ID is always included. Dim id = refNameId.Parent.<OldValue>.Value ' Do something with the data Dim sw As New StringWriter() sw.WriteLine("Work Item {0} was changed.", id) sw.WriteLine("Old Title: " & oldValue) sw.WriteLine("New Title: " & newvalue) el.WriteEntry(sw.ToString()) Else ' The title wasn't changed. ' Do something else End If End If Catch ex As Exception el.WriteEntry(ex.Message, EventLogEntryType.Error) End Try End Sub
請注意,用於創建事件日志的事件源的代碼是在示例客戶端應用程序中定義的,並且需 要首先運行此代碼;否則,用於寫入事件日志的調用將失敗。在實際的應用程序中,應使用安裝程序來定 義事件源。
要使 TFS 可以調用 Web 服務,需為它創建一個事件訂閱。可使用 BisSubscribe 和 以下命令行來實現此操作:
bissubscribe /eventType WorkItemChangedEvent /address http://localhost:8080/MSDNMagTFSEvents/ProcessEvents.asmx /deliveryType Soap /server tfsrtm08
要使用代碼來創建事件訂閱,需編寫類似圖 7 中示例的一些代碼(還需在 示例客戶端應用程序中定義)。
Figure 7 訂閱事件
If mtfs IsNot Nothing Then UpdateStatus(stSubscribeforWebService, True) Dim es As IEventService = _ DirectCast(mtfs.GetService(GetType(IEventService)), IEventService) Dim dp As New DeliveryPreference() dp.Address = String.Format("http://{0}{1}", mtfs.Uri.Authority, _ "/MSDNMagTFSEvents/ProcessEvents.asmx") dp.Schedule = DeliverySchedule.Immediate dp.Type = DeliveryType.Soap Dim filter As String = Nothing If Me.chkOnlySelectedProject.Checked Then filter = String.Format("PortfolioProject = '{0}'", _ Me.cboTeamProjects.SelectedItem.ToString()) End If Try Dim subId As Integer = es.SubscribeEvent( _ mtfs.AuthenticatedUserName, "WorkItemChangedEvent", filter, dp) MsgBox(String.Format(otSubscriptionAdded, subId), _ MsgBoxStyle.Information, otSuccess) Me.btnListMySubs_Click(Me.btnListMySubs, EventArgs.Empty) Catch ex As Exception MsgBox(ex.Message, MsgBoxStyle.Critical, ex.Source) End Try UpdateStatus(stStatus, False) End If
最後細節
與其他事件相關的代碼一樣,需使用有效的 TFS 引用連接到 EventService 服務。然後,創建一個 DeliveryPreference 實例並定義其屬性。本專欄的示例代碼使用 TFS 引用來獲取 TFS 應用程序層(假設示例 Web 服務安裝在其中)的名稱和端口。若實際並非如此,則 需在部署中進行更改。
此代碼將發送時間設為立即;但是,可選擇每天或每周進行發送。在配置 Web 服務訂閱時,需確保使 用 DeliveryType.Soap 選項。
接下來,可定義篩選字符串。示例代碼提供了一個選項來限制特定團隊項目的通知(從窗體的團隊項 目名稱組合框中讀取名稱)。定義完參數後,調用 Subscribe 方法。如果成功,EventService 將返回新 訂閱的 ID。
在構建自己的 Web 服務時,需考慮以下幾點。如果按照本專欄中的說明配置應用程序層上的服務,則 服務將使用 tfsservice 帳戶的安全上下文來運行。這樣做的目的是:必要時,服務可訪問其他特權 TFS 服務。如果沒有這個必要,則可針對自己的解決方案任意選擇其他部署模型(計算機或帳戶)。
第二 個需要注意的問題與創建事件訂閱有關。TFS 僅允許作為 TFS 管理員或 TFS 服務安全組成員的用戶創建 調用 Web 服務的訂閱。
TFS EventService 服務提供了大量隱藏功能和靈活性,可在自己的應用程序中盡情地使用它們。通過 使用此處提供的代碼以及從 Internet 站點(如 CodePlex)獲取的其他代碼,可調整 TFS 以完全適合您 的組織。
與往常一樣,特別感謝 VSTS 團隊提供的解答。尤其是 Microsoft 的 Bill Essary 所提供的寶貴信 息。
請將您想詢問的問題和提出的意見發送至 [email protected].
本文配套源碼:http://www.bianceng.net/dotnet/201212/831.htm