共用方式為


使用 Visual Studio Modelbus 整合模型

Visual Studio ModelBus 提供方法,讓您建立模型間及其他工具至模型的連結。 例如,您可以連結特定領域語言 (DSL) 模型和 UML 模型。 您可以建立一組整合的 DSL。

警告

本文所述的 Visual Studio ModelBus 延伸模組已不再提供下載。 不過,這些指示仍適用於已安裝此延伸模組的使用者。

ModelBus 可讓您建立模型或模型內特定項目的唯一參考。 這個參考可以儲存在模型外,例如儲存在另一個模型的項目中。 在此情況下,工具需要取得項目的存取權,模型匯流排基礎結構會載入適當的模型,並傳回項目。 如有需要,您可以向使用者顯示模型。 如果無法從先前的位置存取檔案,ModelBus 會請使用者尋找檔案。 如果使用者找到檔案,ModelBus 會更新該檔案的所有參考。

注意

在 ModelBus 的目前 Visual Studio 實作中,連結的模型必須是相同 Visual Studio 解決方案中的項目。

如需 ModelBus 延伸模組的詳細資訊,請參閱下列資源:

注意

文字範本轉換元件會作為 Visual Studio 延伸模組開發工作負載的一部分自動安裝。 您也可以從 Visual Studio 安裝程式的 [個別元件] 索引標籤加以安裝,其位於 [SDK、程式庫和架構] 底下。 從 [個別元件] 索引標籤安裝 [模型化 SDK] 元件。

提供 DSL 的存取權

必須先定義適用於 DSL 的 ModelBusAdapter,才能建立模型或其項目的 ModelBus 參考。 執行這項操作最簡單的方式是使用 Visual Studio ModelBus 延伸模組,其會將命令新增至 DSL 設計工具。

將 DSL 定義公開給模型匯流排

  1. 開啟 DSL 定義檔。 以滑鼠右鍵按一下設計介面,然後選取 [啟用 ModelBus]

  2. 在對話方塊中,選擇 [我要將這個 DSL 公開給 ModelBus]。 如果您要將這個 DSL 公開給其模型,又要讓這個 DSL 使用其他 DSL 的參考,您可以選擇兩個選項。

  3. 選取 [確定]。 新專案 ModelBusAdapter 會加入至 DSL 解決方案。

  4. 如果您要從文字範本存取 DSL,您必須修改新專案中的 AdapterManager.tt。 如果您要從其他程式碼 (例如命令和事件處理常式) 存取 DSL,請略過這個步驟。 如需詳細資訊,請參閱 在文字範本中使用 Visual Studio ModelBus

    1. AdapterManagerBase 的基底類別變更為 VsTextTemplatingModelingAdapterManager

    2. 在接近檔案結尾處,將此屬性插入到類別 AdapterManager 的前面:

      [Microsoft.VisualStudio.Modeling.Integration.HostSpecific(HostName)]

    3. ModelBusAdapter 專案的 NuGet 套件管理員中,將 NuGet PackageReference 新增至 Microsoft.VisualStudio.TextTemplating.Modeling

      如果您要同時能夠從文字範本和其他程式碼存取 DSL,您需要一個已修改的配接器和一個未修改的配接器。

  5. 選取 [轉換所有範本]

  6. 重建方案。

    ModelBus 現在可以開啟此 DSL 的執行個體。

    資料夾 ModelBusAdapters\bin* 包含 Dsl 專案和 ModelBusAdapters 專案建置的組件。 若要從另一個 DSL 參考這個 DSL,請匯入這些組件。

確定可參考元素

Visual Studio ModelBus 配接器預設會使用元素的 GUID 來識別。 這些識別碼必須保存在模型檔案中。

確定保存元素識別碼:

  1. 開啟 DslDefinition.dsl

  2. 在 [DSL 總管] 中,依序展開 [XML 序列化行為] 和 [類別資料]

  3. 對於您要建立 ModelBus 參考的每個類別:

    選取類別節點,然後在 [屬性] 視窗中,確定 [序列化識別碼] 已設定為 true

或者,如果您要使用元素名稱 (而不是 GUID) 來識別元素,您可以覆寫產生之配接器的組件。 覆寫配接器類別中的下列方法:

  • 覆寫 GetElementId 以傳回您要使用的 ID。 建立參考時,會呼叫這個方法。
  • 覆寫 ResolveElementReference,以從 ModelBus 參考中找到正確元素。

從另一個 DSL 存取某個 DSL

您可以在 DSL 的網域屬性中儲存 ModelBus 參考,以及撰寫自訂程式碼來使用這些參考。 您也可以讓使用者選擇模型檔案和模型內的某個元素,藉此建立 ModelBus 參考。

若要允許 DSL 使用另一個 DSL 的參考,您應該先將該 DSL 設定為模型匯流排參考的取用者

允許 DSL 使用已公開 DSL 的參考

  1. 在 [DSL 定義] 圖表中,以滑鼠右鍵按一下圖表的主要部分,然後選取 [啟用 ModelBus]

  2. 在對話方塊中,選取 [我要讓這個模型使用模型匯流排參考]

  3. 在取用 DSL 的 Dsl 專案中,將下列組件新增至專案參考。 這些組件 (.dll 檔案) 位於公開 DSL 的 ModelBusAdapter\bin\* 目錄中。

    • 公開的 DSL 組件,例如 Fabrikam.FamilyTree.Dsl.dll

    • 公開的模型匯流排配接器組件,例如 Fabrikam.FamilyTree.ModelBusAdapter.dll

  4. 將下列 .NET 組件加入至使用 DSL 專案的專案參考。

    • Microsoft.VisualStudio.Modeling.Sdk.Integration.dll
    • Microsoft.VisualStudio.Modeling.Sdk.Integration.Shell.dll

在網域屬性中儲存 ModelBusReference

  1. 在使用 DSL 的 DSL 定義中,將網域屬性加入至網域類別並設定其名稱。

  2. 在已選取網域屬性的 [屬性] 視窗中,將 [類型] 設定為 ModelBusReference

    在這個階段,程式碼可以設定屬性值。 值在 [屬性] 視窗中是唯讀的。

    您可以允許使用者使用專用 ModelBusReference 編輯器來設定此屬性。 此編輯器或選擇器有兩種版本。 其中一個版本允許使用者選擇模型檔,另一個版本允許使用者選擇模型檔和模型內的某個項目。

允許使用者設定網域屬性中的 ModelBusReference

  1. 以滑鼠右鍵按一下網域屬性,然後選取 [編輯 ModelBusReference 特定屬性]。 [模型匯流排選擇器] 對話方塊隨即開啟。

  2. 針對模型或模型內的某個元素設定適當的 [ModelBusReference 類型]

  3. 在檔案對話方塊篩選字串中,輸入字串 (例如 Family Tree files |*.ftree)。 替代已公開 DSL 的副檔名。

  4. 如果您選擇模型內某個項目的參考,您可以加入使用者可選取的類型清單,例如 Company.FamilyTree.Person

  5. 選取 [確定],然後選取 [方案總管] 工具列中的 [轉換所有範本]

    警告

    如果您尚未選取有效的模型或實體,[確定] 按鈕即使可能顯示為已啟用,也不會有任何作用。

  6. 如果您指定目標類型清單 (例如 Company.FamilyTree.Person),則必須將組件參考加入至您的 DSL 專案,並參考目標 DSL 的 DLL,例如 Company.FamilyTree.Dsl.dll

測試 ModelBusReference

  1. 建置已公開的 DSL 和使用 DSL。

  2. F5 鍵或 CTRL+F5,在實驗模式中執行其中一個 DSL。

  3. 在 Visual Studio 之實驗執行個體的偵錯專案中,加入每個 DSL 執行個體的檔案。

    注意

    只有在模型是相同 Visual Studio 解決方案中的項目時,Visual Studio ModelBus 才能解析這些模型的參考。 例如,您無法建立針對檔案系統其他部分之模型檔的參考。

  4. 在已公開的 DSL 執行個體中建立一些項目和連結,並加以儲存。

  5. 開啟使用 DSL 的執行個體,然後選取具有模型匯流排參考屬性的模型項目。

  6. 在 [屬性] 視窗中,按兩下模型匯流排參考屬性。 選擇器對話方塊隨即開啟。

  7. 選取 [瀏覽],然後選取已公開 DSL 的執行個體。

    如果已指定特定元素的模型匯流排參考,選擇器也可讓您選擇模型內的某個項目。

在程式碼中建立參考

當您要儲存模型或模型內某個元素的參考時,請建立 ModelBusReferenceModelBusReference 的類型有兩種:模型參考和項目參考。

若要建立模型參考,您需要其模型為執行個體之 DSL 的 AdapterManager,以及模型的檔案名稱或 Visual Studio 專案項目。

若要建立項目參考,您需要模型檔的配接器,以及所要參考的項目。

注意

透過 Visual Studio ModelBus,您可以建立僅針對相同 Visual Studio 解決方案中之項目的參考。

匯入已公開的 DSL 組件

在使用專案中,將專案參考新增至 DSL 和已公開 DSL 的 ModelBusAdapter 組件。

例如,假設您要在 MusicLibrary DSL 的元素中儲存 ModelBus 參考。 ModelBus 參考會參考 FamilyTree DSL 的元素。 在 MusicLibrary 解決方案中 Dsl 專案的 [參考] 節點中,將參考新增至下列組件:

  • Fabrikam.FamilyTree.Dsl.dll。 公開的 DSL。

  • Fabrikam.FamilyTree.ModelBusAdapters.dll 已公開 DSL 的 ModelBus 配接器。

  • Microsoft.VisualStudio.Modeling.Sdk.Integration

  • Microsoft.VisualStudio.Modeling.Sdk.Integration.Shell

    您可以在已公開 DSL 的 ModelBusAdapters 專案中找到這些組件 (位於 *bin\**)。

    在要建立參考的程式碼檔案中,您通常必須匯入這些命名空間:

// The namespace of the DSL you want to reference:
using Fabrikam.FamilyTree;  // Exposed DSL
using Fabrikam.FamilyTree.ModelBusAdapters;
using Microsoft.VisualStudio.Modeling.Integration;
using System.Linq;
...

建立模型的參考

若要建立模型參考,您可以存取已公開 DSL 的 AdapterManager,然後使用 AdapterManager 來建立模型的參考。 您可以指定檔案路徑,或 EnvDTE.ProjectItem

您可以從 AdapterManager 取得配接器,以提供模型內個別項目的存取權。

注意

您必須在配接器使用完畢之後加以處置。 使用 using 陳述式是達成此目標的最便利方式。 說明如下例。

// The file path of a model instance of the FamilyTree DSL:
string targetModelFile = "TudorFamilyTree.ftree";
// Get the ModelBus service:
IModelBus modelBus =
    this.Store.GetService(typeof(SModelBus)) as IModelBus;
// Get an adapterManager for the target DSL:
FamilyTreeAdapterManager manager =
    (modelbus.GetAdapterManager(FamilyTreeAdapter.AdapterId)
     as FamilyTreeAdapterManager;
// or: (modelBus.FindAdapterManagers(targetModelFile).First())
// or could provide an EnvDTE.ProjectItem

// Create a reference to the target model:
// NOTE: the target model must be a file in this project.
ModelBusReference modelReference =
     manager.CreateReference(targetModelFile);
// or can use an EnvDTE.ProjectItem instead of the filename

// Get the root element of this model:
using (FamilyTreeAdapter adapter =
     modelBus.CreateAdapter(modelReference) as FamilyTreeAdapter)
{
  FamilyTree modelRoot = adapter.ModelRoot;
  // Access elements under the root in the usual way:
  foreach (Person p in modelRoot.Persons) {...}
  // You can create adapters for individual elements:
  ModelBusReference elementReference =
     adapter.GetElementReference(person);
  ...
} // Dispose adapter

如果您想在稍後能夠使用 modelReference,您可以將其儲存在具有外部類型 ModelBusReference 的網域屬性中:

using Transaction t = this.Store.TransactionManager
    .BeginTransaction("keep reference"))
{
  artist.FamilyTreeReference = modelReference;
  t.Commit();
}

若要允許使用者編輯這個網域屬性,請使用 ModelReferenceEditor 做為 Editor 屬性中的參數。 如需詳細資訊,請參閱允許使用者編輯參考

建立項目的參考

您針對模型建立的配接器可用來建立及解析參考。

// person is an element in the FamilyTree model:
ModelBusReference personReference =
  adapter.GetElementReference(person);

如果您想在稍後能夠使用 elementReference,您可以將其儲存在具有外部類型 ModelBusReference 的網域屬性中。 若要允許使用者進行編輯,請使用 ModelElementReferenceEditor 做為 Editor 屬性中的參數。 如需詳細資訊,請參閱允許使用者編輯參考

解析參考

如果您具有 ModelBusReference (MBR),您可以取得其參考的模型或模型項目。 如果圖表或其他檢視上顯示此項目,您可以開啟檢視並選取此項目。

您可以從 MBR 建立配接器。 您可以從配接器取得模型根。 您也可以解析參考模型內特定項目的 MBR。

using Microsoft.VisualStudio.Modeling.Integration; ...
ModelBusReference elementReference = ...;

// Get the ModelBus service:
IModelBus modelBus =
    this.Store.GetService(typeof(SModelBus)) as IModelBus;
// Use a model reference or an element reference
// to obtain an adapter for the target model:
using (FamilyTreeAdapter adapter =
   modelBus.CreateAdapter(elementReference) as FamilyTreeAdapter)
   // or CreateAdapter(modelReference)
{
  // Get the root of the model:
  FamilyTree tree = adapter.ModelRoot;

  // Get a model element:
  MyDomainClass mel =
    adapter.ResolveElementReference<MyDomainClass>(elementReference);
  if (mel != null) {...}

  // Get the diagram or other view, if there is one:
  ModelBusView view = adapter.GetDefaultView();
  if (view != null)
  {
   view.Open();
   // Display the diagram:
   view.Show();
   // Attempt to select the shape that presents the element:
   view.SetSelection(elementReference);
  }
} // Dispose the adapter.

解析文字範本中的 ModelBusReferences

您要存取的 DSL 必須具有已設定供文字範本存取的 ModelBus 配接器。 如需詳細資訊,請參閱 提供 DSL 的存取權

一般而言,您會使用儲存在來源 DSL 中的模型匯流排參考 (MBR) 來存取目標 DSL。 因此,您的範本會包含來源 DSL 的指示詞,以及用於解析 MBR 的程式碼。 如需文字範本的詳細資訊,請參閱從特定領域語言產生程式碼

<#@ template debug="true" hostspecific="true"
inherits="Microsoft.VisualStudio.TextTemplating.Modeling.ModelBusEnabledTextTransformation" #>
<#@ SourceDsl processor="SourceDslDirectiveProcessor" requires="fileName='Sample.source'" #>
<#@ output extension=".txt" #>
<#@ assembly name = "Microsoft.VisualStudio.Modeling.Sdk.Integration.11.0" #>
<#@ assembly name = "System.Core" #>
<#@ assembly name = "Company.CompartmentDragDrop.Dsl.dll" #>
<#@ assembly name = "Company.CompartmentDragDrop.ModelBusAdapter.dll" #>
<#@ import namespace="Microsoft.VisualStudio.Modeling.Integration" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="Company.CompartmentDragDrop" #>
<#@ import namespace="Company.CompartmentDragDrop.ModelBusAdapters" #>
<# // Get source root from directive processor:
  ExampleModel source = this.ExampleModel;
  // This DSL has an MBR in its root:
using (ModelBusAdapter adapter = this.ModelBus.CreateAdapter(source.ModelReference) as ModelBusAdapter)
  {
  ModelBusAdapterManager manager = this.ModelBus.FindAdapterManagers(this.Host.ResolvePath("Sample.compDD1")).FirstOrDefault();
  ModelBusReference modelReference =
    manager.CreateReference(this.Host.ResolvePath("Sample.compDD1"));

  // Get the root element of this model:
  using (CompartmentDragDropAdapter adapter =
     this.ModelBus.CreateAdapter(modelReference) as CompartmentDragDropAdapter)
  {
    ModelRoot root = adapter.ModelRoot;
#>
[[<#= root.Name #>]]
<#
  }
#>

如需詳細資訊和逐步解說,請參閱在文字範本中使用 Visual Studio ModelBus

序列化 ModelBusReference

如果您要以字串格式儲存 ModelBusReference (MBR),您可以加以序列化:

string serialized = modelBus.SerializeReference(elementReference);
// Store it anywhere, then get it back again:
ModelBusReference elementReferenceRestored =
    modelBus.DeserializeReference(serialized, null);

以此方式序列化的 MBR 不會影響內容。 如果您使用簡單檔案架構的模型匯流排配接器,MBR 會包含絕對檔案路徑。 如果執行個體模型檔案永遠不會移動,此序列化便已足夠。 不過,模型檔案通常是 Visual Studio 專案中的項目。 您的使用者必須能夠將整個專案移至檔案系統的不同部分。 使用者也必須能夠控制專案的原始檔,並在不同的電腦上開啟專案。 因此,檔案名稱應相對於包含檔案之專案的位置來進行序列化。

相對於指定的檔案路徑來進行序列化

ModelBusReference 包含 ReferenceContext,這是您可以儲存資訊 (例如要序列化的相對檔案路徑) 的字典。

若要相對於路徑進行序列化:

elementReference.ReferenceContext.Add(
   ModelBusReferencePropertySerializer.FilePathSaveContextKey,
   currentProjectFilePath);
string serialized = modelBus.SerializeReference(elementReference);

若要從字串擷取參考:

ReferenceContext context = new ReferenceContext();
context.Add(ModelBusReferencePropertySerializer.FilePathLoadContextKey,
    currentProjectFilePath);
ModelBusReference elementReferenceRestored =
    modelBus.DeserializeReference(serialized, context);

其他配接器建立的 ModelBusReferences

如果您要建立自己的配接器,下列資訊會很實用。

ModelBusReference (MBR) 是由兩個組件所組成:由模型匯流排還原序列化的 MBR 標頭,以及特定配接器管理員處理的特定配接器。 此方法可讓您提供自己的配接器序列化格式。 例如,您可以參考資料庫而不是檔案,或者您可以在配接器參考中儲存其他資訊。 您所擁有的配接器會在 ReferenceContext 中放置其他資訊。

當您還原序列化 MBR 時,您必須提供 ReferenceContext,然後再將其儲存在 MBR 物件中。 當您序列化 MBR 時,配接器會使用儲存的 ReferenceContext 協助產生字串。 還原序列化的字串不包含 ReferenceContext 中的所有資訊。 例如,在簡單檔案架構的配接器中,ReferenceContext 包含根檔案路徑。 此路徑不會儲存在序列化的 MBR 字串中。

MBR 的還原序列化作業可分為兩個階段:

  • ModelBusReferencePropertySerializer 是處理 MBR 標頭的標準序列化程式。 它使用標準 DSL SerializationContext 屬性封包,該封包使用 ReferenceContext 索引鍵儲存在 ModelBusReferencePropertySerializer.ModelBusLoadContextKey 中。 特別要提的是,SerializationContext 應包含 ModelBus 的執行個體。

  • 您的 ModelBus 配接器處理配接器特有的 MBR 部分。 它可以使用儲存在 MBR 之 ReferenceContext 中的其他資訊。 簡單檔案架構的配接器使用下列索引鍵來保存根檔案路徑:FilePathLoadContextKeyFilePathSaveContextKey

    模型檔案中的配接器參考只有在使用時才可還原序列化。

建立模型

建立、開啟及編輯模型

下列程式碼片段取自 VMSDK 網站上的「狀態機器」範例。 此程式碼片段描述如何使用 ModelBusReferences 建立及開啟模型,以及取得與模型相關聯的圖表。

在這個範例中,目標 DSL 的名稱為 StateMachine。 數個名稱會衍生自此名稱,例如模型類別的名稱和 ModelBusAdapter 的名稱。

using Fabrikam.StateMachine.ModelBusAdapters;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
using Microsoft.VisualStudio.Modeling.Integration;
using Microsoft.VisualStudio.Modeling.Integration.Shell;
using Microsoft.VisualStudio.Modeling.Shell;
...
// Create a new model.
ModelBusReference modelReference =
   StateMachineAdapterManager    .CreateStateMachineModel(modelName, fileName);
//Keep reference of new model in this model.
using (Transaction t = ...)
{
  myModelElement.ReferenceProperty = modelReference;
  t.Commit();
}
// Get the ModelBus service from Visual Studio.
IModelBus modelBus = Microsoft.VisualStudio.Shell.Package.
    GetGlobalService(typeof(SModelBus)) as IModelBus;
// Get a modelbus adapter on the new model.
ModelBusAdapter modelBusAdapter;
modelBus.TryCreateAdapter(modelReference,
    this.ServiceProvider, out modelBusAdapter);
using (StateMachineAdapter adapter =
      modelBusAdapter as StateMachineAdapter)
{
    if (adapter != null)
    {
        // Obtain a Diagram from the adapter.
        Diagram targetDiagram =
           ((StandardVsModelingDiagramView)
                 adapter.GetDefaultView()
            ).Diagram;

        using (Transaction t =
             targetDiagram.Store.TransactionManager
                .BeginTransaction("Update diagram"))
        {
            DoUpdates(targetDiagram);
            t.Commit();
        }

        // Display the new diagram.
        adapter.GetDefaultView().Show();
    }
}

驗證參考

BrokenReferenceDetector 會測試存放區中所有可儲存 ModelBusReferences 的網域屬性。 它會在找到您提供的任何動作時,呼叫此動作。 此測試對驗證方法很有用。 下列驗證方法會在嘗試儲存模型時測試存放區,並在錯誤視窗中報告不完整的參考:

[ValidationMethod(ValidationCategories.Save)]
public void ValidateModelBusReferences(ValidationContext context)
{
  BrokenReferenceDetector.DetectBrokenReferences(this.Store,
    delegate(ModelElement element, // parent of property
             DomainPropertyInfo property, // identifies property
             ModelBusReference reference) // invalid reference
    {
      context.LogError(string.Format(INVALID_REF_FORMAT,
             property.Name,
             referenceState.Name,
             new ModelBusReferenceTypeConverter().
                 ConvertToInvariantString(reference)),
         "Reference",
         element);
    });
}
private const string INVALID_REF_FORMAT =
    "The '{0}' domain property of this ReferenceState instance "
  + "named '{1}' contains reference value '{2}' which is invalid";

ModelBus 擴充功能執行的動作

如果您要擴充使用 ModelBus,這些資訊可能會很實用。

ModelBus 擴充功能在 DSL 方案中進行了下列變更。

當您以滑鼠右鍵按一下 DSL 定義圖表,選取 [啟用 ModelBus],然後選取 [啟用這個 DSL 以使用 ModelBus] 時:

  • 在 DSL 專案中,應該將參考新增至 Microsoft.VisualStudio.Modeling.Sdk.Integration.dll

  • 在 DSL 定義中,會加入外部類型參考:Microsoft.VisualStudio.Modeling.Integration.ModelBusReference

    您可以在 [DSL 總管] 的 [網域類型] 下看到這個參考。 若要手動加入外部類型參考,請以滑鼠右鍵按一下根節點。

  • 已新增範本檔案,Dsl\GeneratedCode\ModelBusReferencesSerialization.tt

當您將網域屬性的類型設定為 ModelBusReference,然後以滑鼠右鍵按一下屬性,並選取 [啟用 ModelBusReference 的特定屬性] 時:

  • 數個 CLR 屬性會加入至網域屬性。 您會在 [屬性] 視窗的 [自訂屬性] 欄位中看到這些屬性。 在 Dsl\GeneratedCode\DomainClasses.cs 中,您可以看到屬性宣告上的屬性:

    [System.ComponentModel.TypeConverter(typeof(
    Microsoft.VisualStudio.Modeling.Integration.ModelBusReferenceTypeConverter))]
    [System.ComponentModel.Editor(typeof(
      Microsoft.VisualStudio.Modeling.Integration.Picker
      .ModelReferenceEditor // or ModelElementReferenceEditor
      ), typeof(System.Drawing.Design.UITypeEditor))]
    [Microsoft.VisualStudio.Modeling.Integration.Picker
      .SupplyFileBasedBrowserConfiguration
      ("Choose a model file", "Target model|*.target")]
    

當您以滑鼠右鍵按一下 [DSL 定義圖表],選取 [啟用 ModelBus],然後選取 [將這個 DSL 公開給 ModelBus] 時:

  • 新專案 ModelBusAdapter 會加入至方案。

  • ModelBusAdapter 的參考會加入至 DslPackage 專案。 ModelBusAdapter 會參考 Dsl 專案。

  • DslPackage\source.extention.tt 中,|ModelBusAdapter| 會新增為 MEF 元件。