在文本模板中使用 Visual Studio ModelBus

如果你编写的文本模板读取包含 Visual Studio ModelBus 引用的模型,则可能需要解析引用以访问目标模型。 在这种情况下,你必须调整文本模板和引用的域特定语言 (DSL):

  • 作为引用目标的 DSL 必须具有配置用于从文本模板访问的 ModelBus 适配器。 如果还从其他代码访问 DSL,则除了标准 ModelBus 适配器之外,还需要重新配置的适配器。

    适配器管理器必须从 VsTextTemplatingModelingAdapterManager 继承,并且必须具有属性 [HostSpecific(HostName)]

  • 该模板必须继承自 ModelBusEnabledTextTransformation

注意

如果要读取不包含 ModelBus 引用的 DSL 模型,则可以使用在 DSL 项目中生成的指令处理器。 有关详细信息,请参阅从文本模板中访问模型

有关文本模板的详细信息,请参阅使用 T4 文本模板生成设计时代码

创建模型总线适配器以便从文本模板访问

若要解析文本模板中的 ModelBus 引用,目标 DSL 必须具有兼容的适配器。 文本模板在与 Visual Studio 文档编辑器分离的 AppDomain 中执行,因此适配器必须加载模型,而不是通过 DTE 来访问它。

  1. 如果目标 DSL 解决方案没有 ModelBusAdapter 项目,请使用 Modelbus 扩展向导创建一个项目:

    1. 下载并安装 Visual Studio ModelBus 扩展(如果尚未执行此操作)。 有关详细信息,请参阅可视化和建模 SDK

    2. 打开 DSL 定义文件。 右键单击设计图面,然后单击“启用 Modelbus”。

    3. 在对话框中,选择“我希望向 ModelBus 公开此 DSL”。 如果希望此 DSL 同时公开其模型并使用对其他 DSL 的引用,则可选择这两个选项。

    4. 单击 “确定” 。 新项目“ModelBusAdapter”随即添加到 DSL 解决方案中。

    5. 单击“转换所有模板”。

    6. 重新生成解决方案。

  2. 如果要从文本模板和其他代码(如命令)访问 DSL,请复制 ModelBusAdapter 项目:

    1. 在 Windows 资源管理器中,复制并粘贴包含 ModelBusAdapter.csproj 的文件夹。

    2. 重命名项目文件(例如,重命名为 T4ModelBusAdapter.csproj)。

    3. 在“解决方案资源管理器”中,右键单击解决方案节点,指向“添加”,然后单击“现有项目”。 找到新的适配器项目,T4ModelBusAdapter.csproj。

    4. 在新项目的每个 *.tt 文件中,更改命名空间。

    5. 右键单击“解决方案资源管理器”中的新项目,然后单击“属性”。 在属性编辑器中,更改生成的程序集和默认命名空间的名称。

    6. 在 DslPackage 项目中,添加对新适配器项目的引用,使其同时引用这两个适配器。

    7. 在 DslPackage\source.extension.tt 中,添加一个引用新适配器项目的行。

      <MefComponent>|T4ModelBusAdapter|</MefComponent>
      
    8. 转换所有模板并重新生成解决方案。 不应出现生成错误。

  3. 在新适配器项目中,添加对以下程序集的引用:

    • Microsoft.VisualStudio.TextTemplating.11.0
    • Microsoft.VisualStudio.TextTemplating.Modeling.11.0
  4. 在 AdapterManager.tt 中:

    • 更改 AdapterManagerBase 的声明,使其从 VsTextTemplatingModelingAdapterManager 继承。

      public partial class <#= dslName =>AdapterManagerBase :

      Microsoft.VisualStudio.TextTemplating.Modeling.VsTextTemplatingModelingAdapterManager { ...

    • 在文件末尾附近,替换 AdapterManager 类之前的 HostSpecific 属性。 删除以下行:

      [DslIntegration::HostSpecific(DslIntegrationShell::VsModelingAdapterManager.HostName)]

      插入以下行:

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

      此属性筛选在 modelbus 使用者搜索适配器时可用的适配器集。

  5. 转换所有模板并重新生成解决方案。 不应出现生成错误。

编写可解析 ModelBus 引用的文本模板

通常,从一个模板开始,该模板从“源”DSL 读取和生成文件。 此模板使用源 DSL 项目中生成的指令以从文本模板访问模型中所述的方式读取源模型文件。 但是,源 DSL 包含对“目标”DSL 的 ModelBus 引用。 因此,你需要启用模板代码来解析引用并访问目标 DSL。 因此,必须通过执行以下步骤来改编模板:

  • 将模板的基类更改为 ModelBusEnabledTextTransformation

  • 在模板指令中包含 hostspecific="true"

  • 将程序集引用添加到目标 DSL 及其适配器,并启用 ModelBus。

  • 不需要作为目标 DSL 的一部分生成的指令。

<#@ template debug="true" hostspecific="true" language="C#"
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 = "Company.TargetDsl.Dsl.dll" #>
<#@ assembly name = "Company.TargetDsl.T4ModelBusAdapter.dll" #>
<#@ assembly name = "System.Core" #>
<#@ import namespace="Microsoft.VisualStudio.Modeling.Integration" #>
<#@ import namespace="Company.TargetDsl" #>
<#@ import namespace="Company.TargetDsl.T4ModelBusAdapters" #>
<#@ import namespace="System.Linq" #>
<#
  SourceModelRoot source = this.ModelRoot; // Usual access to source model.
  // In the source DSL Definition, the root element has a model reference:
  using (TargetAdapter adapter = this.ModelBus.CreateAdapter(source.ModelReference) as TargetAdapter)
  {if (adapter != null)
   {
      // Get the root of the target model:
      TargetRoot target = adapter.ModelRoot;
    // The source DSL Definition has a class "SourceElement" embedded under the root.
    // (Let's assume they're all in the same model file):
    foreach (SourceElement sourceElement in source.Elements)
    {
      // In the source DSL Definition, each SourceElement has an MBR property:
      ModelBusReference elementReference = sourceElement.ReferenceToTarget;
      // Resolve the target model element:
      TargetElement element = adapter.ResolveElementReference<TargetElement>(elementReference);
#>
     The source <#= sourceElement.Name #> is linked to: <#= element.Name #> in target model: <#= target.Name #>.
<#
    }
  }}
  // Other useful code: this.Host.ResolvePath(filename) gets an absolute filename
  // from a path that is relative to the text template.
#>

执行此文本模板时,SourceDsl 指令将加载文件 Sample.source。 模板可以从 this.ModelRoot 开始访问该模型的元素。 代码可以使用该 DSL 的域类和属性。

此外,该模板还可以解析 ModelBus 引用。 如果引用指向目标模型,则程序集指令允许代码使用该模型 DSL 的域类和属性。

  • 如果不使用由 DSL 项目生成的指令,则还应包含以下各项。

    <#@ assembly name = "Microsoft.VisualStudio.Modeling.Sdk.11.0" #>
    <#@ assembly name = "Microsoft.VisualStudio.TextTemplating.Modeling.11.0" #>
    
  • 使用 this.ModelBus 获取对 ModelBus 的访问。

演练:测试使用 ModelBus 的文本模板

在本演练中,你将遵循以下步骤:

  1. 构造两个 DSL。 一个 DSL(使用者)有一个 ModelBusReference 属性,该属性可以引用另一个 DSL(提供程序)。

  2. 在提供程序中创建两个 ModelBus 适配器:一个用于由文本模板访问,另一个用于普通代码。

  3. 在单个实验项目中创建 DSL 的实例模型。

  4. 在一个模型中设置一个域属性,使其指向另一个模型。

  5. 编写一个双击处理程序,用于打开指向的模型。

  6. 编写一个文本模板,该模板可以加载第一个模型,然后引用另一个模型,并读取另一个模型。

构造可供 ModelBus 访问的 DSL

  1. 创建新的 DSL 解决方案。 对于本示例,请选择“任务流解决方案”模板。 将语言名称设置为 MBProvider,并将文件扩展名设置为“.provide”。

  2. 在 DSL 定义关系图中,右键单击该关系图不在顶部附近的空白部分,然后单击“启用 Modelbus”。

    如果未看到“启用 Modelbus”,请下载并安装 VMSDK Modelbus 扩展。

  3. 在“启用 Modelbus”对话框中,选择“将此 DSL 公开到 Modelbus”,然后单击“确定”。

    新项目 ModelBusAdapter 已添加到解决方案。

现在,你有了可通过 ModelBus 由文本模板访问的 DSL。 可以在命令、事件处理程序或规则的代码中解析对它的引用,所有这些操作都在模型文件编辑器的 AppDomain 中运行。 但是,文本模板在单独的 AppDomain 中运行,并且在编辑时不能访问模型。 如果要从文本模板访问对此 DSL 的 ModelBus 引用,则必须具有单独的 ModelBusAdapter。

创建为文本模板配置的 ModelBus 适配器

  1. 在文件资源管理器中,复制并粘贴包含 ModelBusAdapter.csproj 的文件夹。

    将文件夹命名为 T4ModelBusAdapter。

    将项目文件重命名为 T4ModelBusAdapter.csproj。

  2. 在“解决方案资源管理器”中,将 T4ModelBusAdapter 添加到 MBProvider 解决方案。 右键单击此解决方案节点,指向“添加”,然后单击“现有项目”。

  3. 右键单击“T4ModelBusAdapter”项目节点,然后单击“属性”。 在项目的属性窗口中,将“程序集名称”和“默认命名空间”更改为 Company.MBProvider.T4ModelBusAdapters

  4. 在 T4ModelBusAdapter 中的每个 *.tt 文件中,将“T4”插入到命名空间的最后一部分,以便行如下所示。

    namespace <#= CodeGenerationUtilities.GetPackageNamespace(this.Dsl) #>.T4ModelBusAdapters

  5. DslPackage 项目中,添加对 T4ModelBusAdapter 的项目引用。

  6. 在 DslPackage\source.extension.tt 中,在 <Content> 下添加以下行。

    <MefComponent>|T4ModelBusAdapter|</MefComponent>

  7. T4ModelBusAdapter 项目中,添加对以下项的引用:Microsoft.VisualStudio.TextTemplating.Modeling.11.0

  8. 打开 T4ModelBusAdapter\AdapterManager.tt:

    1. 将 AdapterManagerBase 的基类更改为 VsTextTemplatingModelingAdapterManager。 文件的这部分内容如下所示。

      namespace <#= CodeGenerationUtilities.GetPackageNamespace(this.Dsl) #>.T4ModelBusAdapters
      {
          /// <summary>
          /// Adapter manager base class (double derived pattern) for the <#= dslName #> Designer
          /// </summary>
          public partial class <#= dslName #>AdapterManagerBase
          : Microsoft.VisualStudio.TextTemplating.Modeling.VsTextTemplatingModelingAdapterManager
          {
      
    2. 在文件末尾附近,将以下附加属性插入到类 AdapterManager 的前面。

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

      结果如下所示。

      /// <summary>
      /// ModelBus modeling adapter manager for a <#= dslName #>Adapter model adapter
      /// </summary>
      [Mef::Export(typeof(DslIntegration::ModelBusAdapterManager))]
      [Mef::ExportMetadata(DslIntegration::CompositionAttributes.AdapterIdKey,<#= dslName #>Adapter.AdapterId)]
      [DslIntegration::HostSpecific(DslIntegrationShell::VsModelingAdapterManager.HostName)]
      [Microsoft.VisualStudio.Modeling.Integration.HostSpecific(HostName)]
      public partial class <#= dslName #>AdapterManager : <#= dslName #>AdapterManagerBase
      {
      }
      
  9. 在“解决方案资源管理器”的标题栏中,单击“转换所有模板”。

  10. F5

  11. 验证 DSL 是否正常工作。 在实验项目中,打开 Sample.provider。 关闭 Visual Studio 的实验实例。

    对此 DSL 的 ModelBus 引用现在可在文本模板和普通代码中解析。

使用 ModelBus 引用域属性构造 DSL

  1. 使用“最小语言”解决方案模板创建新 DSL。 将语言名称设置为 MBConsumer,将文件扩展名设置为“.consume”。

  2. 在 DSL 项目中,添加对 MBProvider DSL 程序集的引用。 右键单击 MBConsumer\Dsl\References,然后单击“添加引用”。 在“浏览”选项卡中,找到 MBProvider\Dsl\bin\Debug\Company.MBProvider.Dsl.dll

    这使你能够创建使用另一个 DSL 的代码。 如果要创建对多个 DSL 的引用,也请进行添加。

  3. 在 DSL 定义关系图中,右键单击该关系图,然后单击“启用 ModelBus”。 在对话框中,选择“启用此 DSL 以使用 ModelBus”。

  4. ExampleElement 类中,添加一个新域属性 MBR,然后在属性窗口中,将其类型设置为 ModelBusReference

  5. 右键单击关系图上的域属性,然后单击“编辑 ModelBusReference 特定属性”。 在对话框中,选择一个模型元素。

    将文件对话框筛选器设置为以下内容。

    Provider File|*.provide

    “|”后的子字符串是“文件选择”对话框的筛选器。 可以使用 *.* 设置它以允许任何文件

    在“模型元素类型”列表中,在提供程序 DSL 中输入一个或多个域类名称(例如 Company.MBProvider.Task)。 它们可以是抽象类。 如果将列表留空,用户可以设置对任意元素的引用。

  6. 关闭对话框并转换所有模板。

    你已创建一个 DSL,该 DSL 可以包含对另一个 DSL 中元素的引用。

创建对解决方案中另一个文件的 ModelBus 引用

  1. 在 MBConsumer 解决方案中,按 CTRL+F5。 Visual Studio 实验实例将在“MBConsumer\Debugging”项目中打开。

  2. 为“MBConsumer\Debugging”项目添加 Sample.provide 副本。 这是必需的,因为 ModelBus 引用必须引用同一解决方案中的文件。

    1. 右键单击调试项目,指向“添加”,然后单击“现有项”。

    2. 在“添加项”对话框中,将筛选器设置为“所有文件(*.*)”。

    3. 导航到 MBProvider\Debugging\Sample.provide,然后单击“添加”。

  3. 打开 Sample.consume

  4. 单击一个示例形状,在“属性”窗口中,单击 MBR 属性中的 [...]。 在对话框中,单击“浏览”并选择 Sample.provide。 在元素窗口中,展开类型 Task 并选择其中一个元素。

  5. 保存文件。 (请不要关闭 Visual Studio 的实验实例。)

    你已创建一个模型,该模型包含对另一个模型中元素的 ModelBus 引用。

解析文本模板中的 ModelBus 引用

  1. 在 Visual Studio 的实验实例中,打开示例文本模板文件。 按如下所示设置其内容。

    <#@ template debug="true" hostspecific="true" language="C#"
    inherits="Microsoft.VisualStudio.TextTemplating.Modeling.ModelBusEnabledTextTransformation" #>
    <#@ MBConsumer processor="MBConsumerDirectiveProcessor" requires="fileName='Sample.consume'" #>
    <#@ output extension=".txt" #>
    <#@ assembly name = "Microsoft.VisualStudio.Modeling.Sdk.Integration.11.0" #>
    <#@ assembly name = "Company.MBProvider.Dsl.dll" #>
    <#@ import namespace="Microsoft.VisualStudio.Modeling.Integration" #>
    <#@ import namespace="Company.MBProvider" #>
    <#
      // Property provided by the Consumer directive processor:
      ExampleModel consumerModel = this.ExampleModel;
      // Iterate through Consumer model, listing the elements:
      foreach (ExampleElement element in consumerModel.Elements)
      {
    #>
       <#= element.Name #>
    <#
        if (element.MBR != null)
      using (ModelBusAdapter adapter = this.ModelBus.CreateAdapter(element.MBR))
      {
              // If we allowed multiple types or DSLs in the MBR, discover type here.
        Task task = adapter.ResolveElementReference<Task>(element.MBR);
    #>
            <#= element.Name #> is linked to Task: <#= task==null ? "(null)" : task.Name #>
    <#
          }
      }
    #>
    
    

    请注意以下内容:

    • 必须设置 template 指令的 hostSpecificinherits 属性。

    • 使用者模型通过该 DSL 中生成的指令处理器以常规方式访问。

    • 程序集和导入指令必须能够访问 ModelBus 和提供程序 DSL 的类型。

    • 如果知道许多 MBR 都链接到同一模型,最好只调用 CreateAdapter 一次。

  2. 保存模板。 验证生成的文本文件是否如下所示。

    ExampleElement1
    ExampleElement2
         ExampleElement2 is linked to Task: Task2
    

解析笔势处理程序中的 ModelBus 引用

  1. 关闭 Visual Studio 的实验实例(如果正在运行)。

  2. 添加一个名为 MBConsumer\Dsl\Custom.cs 的文件,并将其内容设置为以下内容:

    namespace Company.MB2Consume
    {
      using Microsoft.VisualStudio.Modeling.Integration;
      using Company.MB3Provider;
    
      public partial class ExampleShape
      {
        public override void OnDoubleClick(Microsoft.VisualStudio.Modeling.Diagrams.DiagramPointEventArgs e)
        {
          base.OnDoubleClick(e);
          ExampleElement element = this.ModelElement as ExampleElement;
          if (element.MBR != null)
          {
            IModelBus modelbus = this.Store.GetService(typeof(SModelBus)) as IModelBus;
            using (ModelBusAdapter adapter = modelbus.CreateAdapter(element.MBR))
            {
              Task task = adapter.ResolveElementReference<Task>(element.MBR);
              // Open a window on this model:
              ModelBusView view = adapter.GetDefaultView();
              view.Show();
              view.SetSelection(element.MBR);
            }
          }
        }
      }
    }
    
  3. 按 Ctrl+F5。

  4. 在 Visual Studio 的实验实例中,打开 Debugging\Sample.consume

  5. 双击一个形状。

    如果已为该元素设置 MBR,则引用的模型将打开并选中引用的元素。

备注

“文本模板转换”组件将作为“Visual Studio 扩展开发”工作负载的一部分自动安装 。 还可以从 Visual Studio 安装程序的“SDK、库和框架”类别下的“单个组件”选项卡进行安装 。 从“单个组件”选项卡安装“建模 SDK”组件 。