在上一篇文章中,我們已經建立了基本的 DSL 專案結構。現在,我們將深入探討 DSL 設計器在背景中自動產生的程式碼,特別是領域類別和領域關聯的實作細節。理解這些生成的程式碼結構,對於後續進行進階客製化、撰寫驗證規則或開發自訂工具至關重要。
首先,請在 Dsl 專案 中找到 DomainClasses.tt 範本產生的 DomainClasses.cs 檔案。這個檔案主要包含兩個核心類別,分別對應我們在領域模型中定義的根領域類別 ExampleModel 和一般領域類別 ExampleElement。

我們以 ExampleElement 為例,詳細剖析其內部結構,會發現幾個關鍵設計重點:
Guid 型別靜態欄位。這種設計讓關聯類別能直接透過 Guid 參考屬性,同時在驗證邏輯、規則運算中,也經常使用這些 Guid 來代表特定屬性,避免硬編碼字串帶來的維護風險。ExampleElement 與 ExampleModel 之間的嵌入關聯,產生了型別為 ExampleModel 的 ExampleModel 屬性,代表父容器。ExampleElement 自身的參考關聯(重數為 *),產生了強型別集合屬性 LinkedElementCollection<ExampleElement>,方便操作多個目標元素。Name 屬性直接對應領域模型中定義的 Name 領域屬性。接著查看 DomainRelationships.tt 產生的 DomainRelationships.cs。在類別圖中選擇顯示「關聯」或「組合」關係,可以清晰看到關聯類別的結構:

ExampleModelHasElements 為例,它同時持有 ExampleModel(來源)與 ExampleElement(目標)的參考。GetElements(ExampleModel):取得指定 Model 下的所有 ElementsGetExampleModel(ExampleElement):取得 Element 所屬的 ModelGetLink、GetLinks、GetLinksToElements:用於查詢元素間具體的關聯實例理解 DSL 執行期機制,必須熟悉 Store 的角色。它是模型在記憶體中的核心容器:
ModelElement,所有領域關聯繼承自 ElementLink(而 ElementLink 亦繼承自 ModelElement)。因此,Store 中的每個物件本質上都是 ModelElement 實例,我們可透過 Store 統一操作屬性、監聽建立/刪除事件、實作跨領域的驗證邏輯。判斷兩個領域類別應使用嵌入還是參考關聯,最直觀的方式是觀察模型瀏覽器中的呈現:若子元素在樹狀結構中嵌套於父元素之下,應採用嵌入關聯;否則使用參考關聯。
嵌入關聯受限於模型樹的單一父系特性,有嚴格的重數規則:
這些規則雖然嚴格,但設計器內建驗證機制:右鍵選擇 Validate All 即可在錯誤清單中看到違規提示,開發初期可先專注於業務邏輯建模,再逐步修正驗證錯誤。
每個領域類別都應包含一個 Name 領域屬性,並將其 Is Element Name 設為 True。建立類別時,可直接從工具箱拖曳 Named Domain Class 自動附帶此屬性,或手動新增屬性並設定。此屬性值在序列化時作為元素識別依據,也會顯示於模型瀏覽器中,是模型可讀性的關鍵。
掌握上述生成程式碼結構與核心機制,將為後續撰寫自訂驗證、規則、序列化客製化以及開發視覺化擴充套件打下堅實基礎。