Gewusst wie: Generieren von Dateien aus einem UML-Modell

Sie können aus einem UML-Modell Programmcode, Schemas, Dokumente, Ressourcen und beliebige andere Artefakte generieren. Mithilfe von Textvorlagen können Textdateien einfach aus einem UML-Modell generiert werden. Sie ermöglicht es Ihnen, Programmcode in den zu generierenden Text einzubetten.

Drei grundlegende Szenarien sind möglich:

  • Generieren von Dateien mit einem Menübefehl oder einer Geste. Sie definieren einen Visual Studio-Befehl, der für UML-Modelle verfügbar ist.

  • Generieren von Dateien mit einer Anwendung. Sie schreiben eine Anwendung, die UML-Modelle liest und Dateien generiert.

  • Generieren zur Entwurfszeit. Sie definieren mithilfe eines Modells einige der Funktionen der Anwendung und generieren Code, Ressourcen usw. innerhalb der Visual Studio-Projektmappe.

Am Ende dieses Themas wird die Verwendung der Textgenerierung erläutert. Weitere Informationen finden Sie unter Codegenerierung und T4-Textvorlagen.

Generieren von Dateien mit einem Menübefehl

Sie können vorverarbeitete Textvorlagen innerhalb eines UML-Menübefehls verwenden. Im Code der Textvorlage oder in einer separaten partiellen Klasse können Sie das vom Diagramm dargestellte Modell lesen.

Weitere Informationen zu diesen Funktionen finden Sie in den folgenden Themen:

Der im folgenden Beispiel veranschaulichte Ansatz eignet sich für das Generieren von Text aus einem einzelnen Modell, wenn Sie den Vorgang in einem der Modelldiagramme initiieren. Wenn Sie ein Modell in einem separaten Kontext verarbeiten möchten, sollten Sie ggf. Visual Studio-ModelBus für den Zugriff auf das Modell und die zugehörigen Elemente verwenden.

Beispiel

Erstellen Sie zum Ausführen dieses Beispiels ein Visual Studio-Erweiterungsprojekt (VSIX). In diesem Beispiel wird der Projektname VdmGenerator verwendet. Klicken Sie in der Datei source.extension.vsixmanifest auf Inhalt hinzufügen, legen Sie das Typfeld auf MEF-Komponente fest, und verweisen Sie mit dem Quellpfad auf das aktuelle Projekt. Weitere Informationen zum Einrichten dieses Projekttyps finden Sie unter Gewusst wie: Definieren eines Menübefehls in einem Modellierungsdiagramm.

Fügen Sie dem Projekt eine C#-Datei mit dem folgenden Code hinzu. Diese Klasse definiert einen Menübefehl, der für ein UML-Klassendiagramm angezeigt wird.

using System;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;

namespace VdmGenerator
{
  [Export(typeof(ICommandExtension))]
  [ClassDesignerExtension]
  public class GenerateVdmFromClasses : ICommandExtension
  {
    [Import] public IDiagramContext DiagramContext { get; set; }
    public void Execute(IMenuCommand command)
    {
      // Initialize the template with the Model Store.
      VdmGen generator = new VdmGen(
             DiagramContext.CurrentDiagram.ModelStore);
      // Generate the text and write it.
      System.IO.File.WriteAllText
        (System.IO.Path.Combine(
            Environment.GetFolderPath(
                Environment.SpecialFolder.Desktop),
            "Generated.txt") 
         , generator.TransformText());
    }
    public void QueryStatus(IMenuCommand command)
    {
      command.Enabled = command.Visible = true;
    }
    public string Text
    { get { return "Generate VDM"; } }
  }
}

Die folgende Datei ist die Textvorlage. Sie generiert eine Textzeile für jede UML-Klasse im Modell und eine Zeile für jedes Attribut in jeder Klasse. Code zum Lesen des Modells ist durch <# ... #> begrenzt in den Text eingebettet.

Klicken Sie zum Erstellen dieser Datei im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, zeigen Sie auf Hinzufügen, und klicken Sie dann auf Neues Element. Wählen Sie Vorverarbeitete Textvorlage aus. Für dieses Beispiel sollte der Dateiname VdmGen.tt lauten. Die Eigenschaft Benutzerdefiniertes Tool der Datei sollte auf TextTemplatingFilePreprocessor festgelegt sein. Weitere Informationen zu vorverarbeiteten Textvorlagen finden Sie unter Generieren von Text zur Laufzeit mithilfe von vorverarbeiteten T4-Textvorlagen.

<#@ import namespace="Microsoft.VisualStudio.Uml.Classes" #>
<# 
   foreach (IClass classElement in store.AllInstances<IClass>())
   {
#>
Type <#= classElement.Name #> ::
<#
     foreach (IProperty attribute in classElement.OwnedAttributes)
     {
#>
       <#= attribute.Name #> : <#= 
           attribute.Type == null ? ""
                                  : attribute.Type.Name #> 
<#
     }
   }
#>

Die Textvorlage generiert eine partielle C#-Klasse, die Teil des Visual Studio-Projekts wird. Fügen Sie in einer separaten Datei eine weitere partielle Deklaration derselben Klasse hinzu. Durch diesen Code kann die Vorlage auf den UML-Modellspeicher zugreifen:

using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
namespace VdmGenerator
{
    public partial class VdmGen
    {
        private IModelStore store;
        public VdmGen(IModelStore s)
        { store = s; }
    }
}

Drücken Sie zum Testen des Projekts F5. Eine neue Instanz von Visual Studio wird gestartet. Öffnen oder erstellen Sie in dieser Instanz ein UML-Modell, das ein Klassendiagramm enthält. Fügen Sie dem Diagramm einige Klassen hinzu, und fügen Sie jeder Klasse einige Attribute hinzu. Klicken Sie mit der rechten Maustaste in das Diagramm, und klicken Sie dann auf den Beispielbefehl Generate VDM. Durch den Befehl wird die Datei C:\Generated.txt erstellt. Überprüfen Sie diese Datei. Der Inhalt sollte dem folgenden Text ähneln, aber Ihre eigenen Klassen und Attribute enthalten:

Type Class1 ::
          Attribute1 : int 
          Attribute2 : string 
Type Class2 :: 
          Attribute3 : string 

Generieren von Dateien mit einer Anwendung

Sie können Dateien mit einer Anwendung generieren, die ein UML-Modell liest. Die flexibelste und stabilste Methode für den Zugriff auf das Modell und die zugehörigen Elemente ist für diesen Zweck Visual Studio-ModelBus.

Sie können das Modell auch mithilfe der grundlegenden API laden und mit den im vorherigen Abschnitt beschriebenen Techniken an Textvorlagen übergeben. Weitere Informationen zum Laden eines Modells finden Sie unter Gewusst wie: Lesen eines UML-Modells im Programmcode.

Generieren von Dateien zur Entwurfszeit

Wenn im Projekt eine Standardmethode für die Interpretation der UML als Code definiert ist, können Sie Textvorlagen erstellen, mit denen Sie im Projekt Code aus einem UML-Modell generieren können. Normalerweise arbeiten Sie mit einer Projektmappe, die das UML-Modellprojekt enthält, und mindestens einem Projekt für den Anwendungscode. Jedes Codeprojekt kann mehrere Vorlagen enthalten, die basierend auf dem Inhalt des Modells Programmcode, Ressourcen und Konfigurationsdateien generieren. Der Entwickler kann alle Vorlagen ausführen, indem er auf der Symbolleiste des Projektmappen-Explorers auf Alle Vorlagen transformieren klickt. Programmcode wird normalerweise in Form von partiellen Klassen generiert, um die Integration manuell geschriebener Teile zu vereinfachen.

Ein Visual Studio-Projekt dieser Art kann in Form einer Vorlage verteilt werden, sodass jedes Mitglied eines Teams auf die gleiche Weise Projekte erstellen kann, die Code aus einem Modell generieren. Normalerweise ist die Vorlage Teil eines Erweiterungspakets, das Validierungseinschränkungen für das Modell beinhaltet. Durch diese Einschränkungen wird sichergestellt, dass die Vorbedingungen des Generierungscodes erfüllt werden.

Verfahren zum Generieren von Dateien

  • Wählen Sie im Dialogfeld "Neue Datei hinzufügen" die Option Textvorlage aus, um einem Projekt eine Vorlage hinzuzufügen. Den meisten Projekttypen können Vorlagen hinzugefügt werden, bei Modellierungsprojekten ist dies jedoch nicht möglich.

  • Die Eigenschaft "Benutzerdefiniertes Tool" der Vorlagendatei sollte auf TextTemplatingFileGenerator festgelegt sein, und die Dateinamenerweiterung muss ".tt" lauten.

  • Die Vorlage sollte mindestens eine Ausgabedirektive enthalten:

    <#@ output extension=".cs" #>

    Legen Sie das Erweiterungsfeld entsprechend der Sprache des Projekts fest.

  • Damit der Generierungscode in der Vorlage auf das Modell zugreifen kann, schreiben Sie <#@ assembly #>-Direktiven für die zum Lesen eines UML-Modells benötigten Assemblys. Verwenden Sie ModelingProject.LoadReadOnly(), um das Modell zu öffnen. Weitere Informationen finden Sie unter Gewusst wie: Lesen eines UML-Modells im Programmcode.

  • Die Vorlage wird ausgeführt, wenn Sie sie speichern und wenn Sie auf der Symbolleiste des Projektmappen-Explorers auf Alle Vorlagen transformieren klicken.

  • Weitere Informationen zu diesem Vorlagentyp finden Sie unter Generieren von Code zur Entwurfszeit mithilfe von T4-Textvorlagen.

  • In einem typischen Projekt werden mehrere Vorlagen verwendet, die unterschiedliche Dateien aus demselben Modell generieren. Der erste Teil jeder Vorlage ist identisch. Sie können diese Codeduplizierung reduzieren, indem Sie die allgemeinen Teile in eine separate Textdatei verschieben, und diese dann mit der <#@include file="common.txt"#>-Direktive in jeder Vorlage aufrufen.

  • Sie können auch einen spezialisierten Direktivenprozessor definieren, mit dem Sie Parameter für den Textgenerierungsprozess bereitstellen. Weitere Informationen finden Sie unter Anpassen der T4-Texttransformation.

Beispiel

In diesem Beispiel wird eine C#-Klasse für jede UML-Klasse im Quellmodell generiert.

So richten Sie eine Visual Studio-Projektmappe für dieses Beispiel ein

  1. Erstellen Sie in einem Modellierungsprojekt in einer neuen Projektmappe ein UML-Klassendiagramm.

    1. Klicken Sie im Menü Architektur auf Neues Diagramm.

    2. Wählen Sie UML-Klassendiagramm aus.

    3. Folgen Sie den Eingabeaufforderungen, um eine neue Projektmappe und ein Modellierungsprojekt zu erstellen.

    4. Fügen Sie dem Diagramm einige Klassen hinzu, indem Sie das UML-Klassentool aus der Toolbox ziehen.

    5. Speichern Sie die Datei.

  2. Erstellen Sie ein C#- oder Visual Basic-Projekt in derselben Projektmappe.

    • Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf die Projektmappe, zeigen Sie auf Hinzufügen, und klicken Sie anschließend auf Neues Projekt. Klicken Sie unter Installierte Vorlagen auf Visual Basic oder Visual C#, und wählen Sie dann einen Projekttyp aus, z. B. Konsolenanwendung.
  3. Fügen Sie dem C#- oder Visual Basic-Projekt eine Nur-Text-Datei hinzu. Diese Datei enthält Code, der beim Schreiben mehrerer Textvorlagen freigegeben wird.

    • Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, zeigen Sie auf Hinzufügen, und klicken Sie anschließend auf Neues Element. Wählen Sie Textdatei aus.

    Fügen Sie den im folgenden Abschnitt dargestellten Text ein.

  4. Fügen Sie dem C#- oder Visual Basic-Projekt eine Textvorlagendatei hinzu.

    • Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, zeigen Sie auf Hinzufügen, und klicken Sie anschließend auf Neues Element. Wählen Sie Textvorlage aus.

    Fügen Sie den folgenden Code in die Textvorlagendatei ein.

  5. Speichern Sie die Textvorlagendatei.

  6. Überprüfen Sie den Code in der untergeordneten Datei. Er sollte eine Klasse für jede UML-Klasse im Modell enthalten.

    1. Klicken Sie in einem Visual Basic-Projekt auf der Symbolleiste des Projektmappen-Explorers auf Alle Dateien anzeigen.

    2. Erweitern Sie im Projektmappen-Explorer den Vorlagendateiknoten.

Inhalt der freigegebenen Textdatei

In diesem Beispiel lautet der Dateiname "SharedTemplateCode.txt", und die Datei befindet sich im gleichen Ordner wie die Textvorlagen.

<# /* Common material for inclusion in my model templates */ #>
<# /* hostspecific allows access to the Visual Studio API */ #>
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="Microsoft.VisualStudio.Uml.Interfaces.dll"#>
<#@ assembly name="Microsoft.VisualStudio.ArchitectureTools.Extensibility.dll"#>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="Microsoft.VisualStudio.Uml.Classes" #>
<#@ import namespace="Microsoft.VisualStudio.ArchitectureTools.Extensibility" #>
<#@ import namespace="Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml" #>
<#+  // Note this is a Class Feature Block
///<summary>
/// Text templates are run in a common AppDomain, so 
/// we can cache the model store that we find.
///</summary>
private IModelStore StoreCache
{
  get { return AppDomain.CurrentDomain.GetData("ModelStore") as IModelStore; }
  set { AppDomain.CurrentDomain.SetData("ModelStore", value); } 
}
private bool CacheIsOld()
{
    DateTime? dt = AppDomain.CurrentDomain
           .GetData("latestAccessTime") as DateTime?;
    DateTime t = dt.HasValue ? dt.Value : new DateTime(); 
    DateTime now = DateTime.Now;
    AppDomain.CurrentDomain.SetData("latestAccessTime", now);
    return now.Subtract(t).Seconds > 3;
}

///<summary>
/// Find the UML modeling project in this solution,
/// and load the model.
///</summary>
private IModelStore ModelStore
{
  get 
  {
    // Avoid loading the model for every template:
    if (StoreCache == null || CacheIsOld())
    {
      // Use Visual Studio API to find modeling project:
      EnvDTE.DTE dte = (EnvDTE.DTE) ((IServiceProvider) this.Host)
                       .GetService(typeof(EnvDTE.DTE));
      EnvDTE.Project project = null;
      foreach (EnvDTE.Project p in dte.Solution.Projects)
      {
        if (p.FullName.EndsWith(".modelproj"))
        {
          project = p;
          break;
        }            
      }
      if (project == null) return null;

      // Load UML model into this AppDomain
      // and access model store:
      IModelingProjectReader reader = 
           ModelingProject.LoadReadOnly(project.FullName);
      StoreCache = reader.Store;
    }
    return StoreCache;
  }
}
#>

Inhalt der Textvorlagendatei

Der folgende Text wird in die .tt-Datei eingefügt. In diesem Beispiel werden Klassen in einer C#-Datei aus den UML-Klassen im Modell generiert. Sie können jedoch Dateien eines beliebigen Typs generieren. Die Sprache der generierten Datei hängt nicht mit der Sprache zusammen, in der der Textvorlagencode geschrieben ist.

<#@include file="SharedTemplateCode.txt"#>
<#@ output extension=".cs" #>
namespace Test
{
<#
      foreach (IClass c in ModelStore.AllInstances<IClass>())
      {
#>
   public partial class <#=c.Name#>
   {   }
<#
      }
#>
}

Verwendung der Textgenerierung

Die Vorteile und Möglichkeiten der Modellierung lassen sich am besten nutzen, wenn Sie Modelle für den Entwurf auf der Anforderungs- oder Architekturebene verwenden. Allgemeine Ideen und Konzepte auf hoher Entwurfsebene können zum Teil mithilfe von Textvorlagen in Code konvertiert werden. In vielen Fällen führt dies nicht zu einer 1:1-Entsprechung zwischen den Elementen in den UML-Modellen und Klassen oder anderen Teilen des Programmcodes.

Zudem hängt die Transformation vom Problembereich ab. Es gibt keine universelle Zuordnung zwischen Modellen und Code.

Im Folgenden sehen Sie einige Beispiele für die Generierung von Code aus Modellen:

  • Produktgruppen. Fabrikam, Inc. entwickelt und installiert Gepäckabfertigungssysteme für Flughäfen. Ein Großteil der Software für die unterschiedlichen Installationen ist identisch, die Softwarekonfiguration hängt jedoch von den installierten Gepäcktransportsystemen und der Verschaltung dieser Teile mit den Fließbändern ab. Zu Beginn eines Vertrags besprechen die Analytiker von Fabrikam die Anforderungen mit der Flughafenverwaltung, und sie erfassen den Hardwareplan mithilfe eines UML-Aktivitätsdiagramms. Aus diesem Modell generiert das Entwicklungsteam Konfigurationsdateien, Programmcode, Pläne und Benutzerdokumente. Den Abschluss der Arbeit bilden manuelle Ergänzungen und Anpassungen des Codes. Da das Team von Auftrag zu Auftrag Erfahrung sammelt, erweitern es den Umfang des generierten Materials.

  • Muster. Die Entwickler bei Contoso Ltd. erstellen oft Websites und verwenden dabei UML-Klassendiagramme zum Entwerfen des Navigationsschemas. Jede Webseite wird durch eine Klasse dargestellt, und Zuordnungen stellen Navigationslinks dar. Die Entwickler generieren ein Großteil des Codes einer Website aus dem Modell. Jede Webseite entspricht mehreren Klassen und Ressourcendateieinträgen. Diese Methode bietet den Vorteil, dass die Konstruktion jeder Seite nur einem Muster entspricht. Sie ist daher zuverlässiger und flexibler als handgeschriebener Code. Das Muster ist in den Generierungsvorlagen enthalten, während das Modell zum Erfassen der variablen Aspekte verwendet wird.

  • Schemas. Humongous Insurance arbeitet mit Tausenden von Systemen in der ganzen Welt. Diese Systeme verwenden unterschiedliche Datenbanken, Sprachen und Schnittstellen. Das zentrale Architekturteam veröffentlicht Modelle von Geschäftskonzepten und Prozessen intern. Lokale Teams generieren aus diesen Modellen Teile ihrer Datenbank und tauschen Schemas, Deklarationen usw. im Programmcode aus. Die grafische Darstellung der Modelle erleichtert den Teams die Diskussion von Vorschlägen. Die Teams erstellen mehrere Diagramme, die für unterschiedliche Geschäftsbereiche geltende Teilmengen des Modells zeigen. Von Änderungen betroffene Bereiche werden zudem farblich hervorgehoben.

Wichtige Techniken für das Generieren von Artefakten

In den obigen Beispielen werden Modelle für verschiedene geschäftliche Zwecke verwendet, und die Interpretation der Modellierungselemente wie Klassen und Aktivitäten variiert von Anwendung zu Anwendung. Die folgenden Techniken sind beim Generieren von Artefakten aus Modellen hilfreich.

  • Profile. Selbst innerhalb eines Geschäftsbereichs kann die Interpretation eines Elementtyps variieren. In einem Websitediagramm können z. B. einige Klassen Webseiten darstellen, während andere Inhaltsblöcke darstellen. Definieren Sie Stereotype, um den Benutzern diese Unterscheidungen zu erleichtern. Stereotype bieten zudem die Möglichkeit, zusätzliche Eigenschaften anzufügen, die für Elemente dieser Art gelten. Stereotype werden in Profilen verpackt. Weitere Informationen finden Sie unter Gewusst wie: Definieren eines Profils zum Erweitern von UML.

    Der Zugriff auf die für ein Objekt definierten Stereotype in Vorlagencode ist einfach. Beispiel:

    public bool HasStereotype(IClass c, string profile, string stereo)
    { return c.AppliedStereotypes.Any
       (s => s.Profile == profile && s.Name == stereo ); }
    
  • Eingeschränkte Modelle. Es ist möglich, dass Modelle erstellt werden, die für den jeweiligen Verwendungszweck ungültig sind. Bei den Flughafengepäckmodellen von Fabrikam wäre es z. B. falsch, einen Check-In-Schalter ohne ausgehendes Förderband zu entwerfen. Sie können Validierungsfunktionen definieren, mit deren Hilfe Benutzer diese Einschränkungen einhalten können. Weitere Informationen finden Sie unter Gewusst wie: Definieren von Validierungseinschränkungen für UML-Modelle.

  • Beibehalten manueller Änderungen. Nur einige der Projektmappendateien können aus einem Modell generiert werden. In den meisten Fällen muss es möglich sein, den generierten Inhalt manuell zu ergänzen oder anzupassen. Es ist jedoch wichtig, dass diese manuellen Änderungen beibehalten werden, wenn die Vorlagentransformation erneut ausgeführt wird.

    Vorlagen, die Code in .NET-Sprachen generieren, sollten partielle Klassen generieren, damit Entwickler Methoden und Code hinzufügen können. Es ist auch hilfreich, jede Klasse als Paar zu generieren: eine abstrakte Basisklasse, die die Methoden enthält, und eine erbende Klasse, die nur den Konstruktor enthält. Dadurch können Entwickler die Methoden überschreiben. Damit die Initialisierung überschrieben werden kann, wird sie nicht in den Konstruktoren, sondern in einer separaten Methode eingefügt.

    Bei einer Vorlage, die XML und andere Ausgabetypen generiert, kann es schwieriger sein, den manuellen Inhalt vom generierten Inhalt zu trennen. Eine Methode besteht darin, eine Aufgabe im Buildprozess zu erstellen, die zwei Dateien kombiniert. Eine andere Methode ist das Anpassen einer lokale Kopie der Generierungsvorlage.

  • Verschieben von Code in separate Assemblys. Es wird davon abgeraten, umfangreiche Codetexte in Vorlagen zu schreiben. Trennen Sie generierten Inhalt möglichst von der Berechnungslogik. Textvorlagen eignen sich nicht gut zum Bearbeiten von Code.

    Wenn Sie umfangreiche Berechnungen zum Generieren von Text ausführen müssen, sollten Sie diese Funktionen stattdessen in einer separaten Assembly erstellen und die entsprechenden Methoden in der Vorlage aufrufen.