首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

Visual Studio DSL 入門 5:深入解析生成的領域類別與領域關聯

     編輯:關於.NET

在上一篇文章中,我們已經建立了基本的 DSL 專案結構。現在,我們將深入探討 DSL 設計器在背景中自動產生的程式碼,特別是領域類別和領域關聯的實作細節。理解這些生成的程式碼結構,對於後續進行進階客製化、撰寫驗證規則或開發自訂工具至關重要。

一、領域類別的生成結構

首先,請在 Dsl 專案 中找到 DomainClasses.tt 範本產生的 DomainClasses.cs 檔案。這個檔案主要包含兩個核心類別,分別對應我們在領域模型中定義的根領域類別 ExampleModel 和一般領域類別 ExampleElement。

DomainClasses.cs 類別圖

我們以 ExampleElement 為例,詳細剖析其內部結構,會發現幾個關鍵設計重點:

  1. 識別欄位使用 Guid 型別:每個領域屬性在類別中都有一個對應的 Guid 型別靜態欄位。這種設計讓關聯類別能直接透過 Guid 參考屬性,同時在驗證邏輯、規則運算中,也經常使用這些 Guid 來代表特定屬性,避免硬編碼字串帶來的維護風險。
  2. 關聯關係對應的導覽屬性:
    • 嵌入關聯:ExampleElement 與 ExampleModel 之間的嵌入關聯,產生了型別為 ExampleModel 的 ExampleModel 屬性,代表父容器。
    • 參考關聯:ExampleElement 自身的參考關聯(重數為 *),產生了強型別集合屬性 LinkedElementCollection<ExampleElement>,方便操作多個目標元素。
  3. 領域屬性映射:類別中的 Name 屬性直接對應領域模型中定義的 Name 領域屬性。

二、領域關聯的生成結構

接著查看 DomainRelationships.tt 產生的 DomainRelationships.cs。在類別圖中選擇顯示「關聯」或「組合」關係,可以清晰看到關聯類別的結構:

DomainRelationships.cs 類別圖

  1. Guid 為核心連結機制:領域關聯類別透過 Guid 欄位關聯來源與目標領域類別,確保模型元素之間的連結在序列化與反序列化過程中保持穩定。
  2. 雙向導覽屬性:每個關聯類別都分別儲存了來源與目標角色對應的屬性。以 ExampleModelHasElements 為例,它同時持有 ExampleModel(來源)與 ExampleElement(目標)的參考。
  3. 實用的靜態輔助方法:關聯類別提供了豐富的靜態方法,極大簡化了模型操作:
    • GetElements(ExampleModel):取得指定 Model 下的所有 Elements
    • GetExampleModel(ExampleElement):取得 Element 所屬的 Model
    • GetLink、GetLinks、GetLinksToElements:用於查詢元素間具體的關聯實例

三、核心執行機制:Store(存儲)

理解 DSL 執行期機制,必須熟悉 Store 的角色。它是模型在記憶體中的核心容器:

  1. 模型元素的中央倉庫:執行期間,所有模型元素與關聯實例皆儲存在 Store 中。Store 提供建立、刪除、Undo/Redo、規則引擎、事件通知等完整生命週期管理功能,是 DSL 開發中最頻繁互動的核心物件。
  2. 模型載入流程:當 .dsl 模型檔案被開啟,系統會自動建立新的 Store 實例,並將檔案中的所有模型元素與關聯實例還原載入。這個反序列化過程涉及分割合併演算法,後續文章將詳細介紹。
  3. 統一的繼承體系:所有領域類別繼承自 ModelElement,所有領域關聯繼承自 ElementLink(而 ElementLink 亦繼承自 ModelElement)。因此,Store 中的每個物件本質上都是 ModelElement 實例,我們可透過 Store 統一操作屬性、監聽建立/刪除事件、實作跨領域的驗證邏輯。

四、實務開發中的關鍵細節與規則

1. 嵌入 vs 參考關聯的判斷準則

判斷兩個領域類別應使用嵌入還是參考關聯,最直觀的方式是觀察模型瀏覽器中的呈現:若子元素在樹狀結構中嵌套於父元素之下,應採用嵌入關聯;否則使用參考關聯。

2. 嵌入關聯的重數限制

嵌入關聯受限於模型樹的單一父系特性,有嚴格的重數規則:

  • 目標角色重數只能是 1..1 (One) 或 0..1 (ZeroOne),因為一個 ModelElement 只能被嵌入一次。
  • 若一個 ModelElement 同時是多個嵌入關聯的目標角色,所有目標角色重數必須為 0..1,因為它同一時間只能屬於一個父容器。
  • 在完整領域模型中,除根類別外,每個領域類別必須是某個嵌入關聯的目標角色,否則無法構成完整模型樹,導致序列化失敗。

這些規則雖然嚴格,但設計器內建驗證機制:右鍵選擇 Validate All 即可在錯誤清單中看到違規提示,開發初期可先專注於業務邏輯建模,再逐步修正驗證錯誤。

3. Name 領域屬性的必要性

每個領域類別都應包含一個 Name 領域屬性,並將其 Is Element Name 設為 True。建立類別時,可直接從工具箱拖曳 Named Domain Class 自動附帶此屬性,或手動新增屬性並設定。此屬性值在序列化時作為元素識別依據,也會顯示於模型瀏覽器中,是模型可讀性的關鍵。

掌握上述生成程式碼結構與核心機制,將為後續撰寫自訂驗證、規則、序列化客製化以及開發視覺化擴充套件打下堅實基礎。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved