In Part 1 und Part 2 der Serie habe ich schon die Generierung von Code aus Visual Studio Modeling Projects und die Anpassung der T4-Templates beschrieben. In diesem Teil geht es um die Erstellung eines eigenen “Profiles” mit neuen Stereotypen.
Ein eigenes Profile muss als Visual Studio Extension (.vsix) erstellt werden. Dazu ist das Visual Studio SDK nötig. Ist dieses installiert, dann kann über den Dialog “Neues Projekt” eine neues “Visual Studio Package” erstellt werden. Die Angaben in Wizzard sind eigentlich selbsterklärend. Da das Paket nur als Container benötigt wird, können fast alle Optionen deaktiviert werde.
Dem Projekt muss man jetzt eine XML-Datei mit der Endung .profile hinzufügen. Die Option “Copy to Output Directory” setzt man auf “Copy if newer”, damit die Datei in das Ausgabeverzeichnis kopiert wird.
Der Inhalt der Datei muss folgendes “Gerüst haben”:
<?xml version="1.0" encoding="utf-8" ?> <profile dslVersion="1.0.0.0" name="modelFirstProfile" displayName="Model First Profile" xmlns="http://schemas.microsoft.com/UML2.1.2/ProfileDefinition"> <stereotypes> ... </stereotypes> <metaclasses> <!-- List of metaclasses (that is, classes in the UML spec) that are mentioned in the stereotype definitions. --> <metaclass name="Microsoft.VisualStudio.Uml.Classes.IClass"/> <metaclass name="Microsoft.VisualStudio.Uml.Classes.IInterface"/> <metaclass name="Microsoft.VisualStudio.Uml.Classes.IProperty" /> </metaclasses> <propertyTypes> <!-- List of external or enumeration types that are mentioned in the stereotype property definitions. --> <externalType name="System.String" /> <externalType name="System.Boolean" /> </propertyTypes> </profile>
Die Eigenschaft “Name” (Zeile 3) ist sehr wichtig, die diese innerhalb der Stereotypen verwendet wird. In dem Abschnitt “metaclasses” legt man die Objekte fest, die man durch eigene Stereotypen erweitern möchte Die Objekte stammen aus dem Namensraum Microsoft.VisualStudio.Uml.Classes (http://msdn.microsoft.com/de-de/library/microsoft.visualstudio.uml.classes.aspx).
Die Stereotypen haben folgende Syntax:
<stereotype name="Entity" displayName="Entity Class"> <metaclasses> <metaclassMoniker name="/modelFirstProfile/Microsoft.VisualStudio.Uml.Classes.IClass" /> </metaclasses> <properties> <property name="CustomAttribute" displayName="Custom Attribute"> <propertyType> <externalTypeMoniker name="/modelFirstProfile/System.String" /> </propertyType> </property> </properties> </stereotype>
Die metaclassMoniker verweisen auf die metaclasses. Wichtig ist, dass diese die Form /<name_profile>/<name_metaclass> aufweisen. Diese geben an, auf welche Objekte der Stereotype angewendet wird.
Unter “properties” kann man dann eigen Eigenschaften anlegen. Die Syntax der “externalMoniker” ist dieselbe wie bei den Metaklassen.
Die Stereotyp “Entity” erweitert also alle Objekte von Typ Klasse und stellt ein eigenes Text-Property zu Verfügung.
Damit das Beispiel etwas praxisorientiert ist, füge ich noch eine neue Eigenschaft für Propperties hinzu. Dies sollen als “INotifyPropertyChanged” deklariert werden können um automatsich Models für WPF, Silverlight etc. erstellen zu können.
<stereotype name="EntityProperty" displayName="Entity Property"> <metaclasses> <metaclassMoniker name="/modelFirstProfile/Microsoft.VisualStudio.Uml.Classes.IProperty" /> </metaclasses> <properties> <property name="IsNotifyChangedProperty" displayName="IsNotifyChangedProperty" defaultValue="false"> <propertyType> <externalTypeMoniker name="/modelFirstProfile/System.Boolean" /> </propertyType> </property> </properties> </stereotype>
Damit das Profil installiert wird, muss naoch die Datei source.extension.vsixmanifest angepasst werden. Unter Assets muss das Profile hinuzgefügt werden. Der Typ “Microsoft.VisualStudio.UmlProfile” ist nicht in der Dropdown-Liste und muss manuell eingegeben werden. Andere Assets können entfernt werden.
Nach einem Build steht die Extension nun im Ausgabeverzeichnis bereit und kann installiert werden. Falls es Probleme gibt, kann man mit F5 die Extension debuggen und erhält so hinweise auf mögliche Fehler.
Nach der Installation wählt man auf einem Package im “UML Model Explorer” das neue Profil aus. Danach stehen die neuen Eigenschaften auf den Objekten zur Verfügung.
Jetzt können die T4-Templates entsprechend erweitert werden. Mit folgendem Code kann z.B. getestet werden, ob ein Element einen bestimmten Stereotyp aufweist.
private static readonly string AlegriProfileName = "modelFirstProfile"; private static string GetAlegriStereotype(IElement element) { var stereotypes = element.AppliedStereotypes.Where(s => s.Profile == AlegriProfileName); if(stereotypes.Count() == 1) { return stereotypes.First() != null ? stereotypes.First().Name : null; } return null; }
Mit folgendem Code kann die Eigenschaft “IsPropertyNotifiedChanged” ausgelesen werden.
private static string GetModFirstProperty(IElement element, string stereotypeName, string property) { return element.GetStereotypeProperty(ModFirstProfileName, stereotypeName, property) ?? string.Empty; } private static bool PropertyIsNotifyPropertyChanged(IProperty property) { string isNotifyPropertyChangedValue = GetModFirstProperty(property, "EntityProperty", "IsNotifyChangedProperty"); bool isNotifyPropertyChanged = false; if(!string.IsNullOrEmpty(isNotifyPropertyChangedValue)) { isNotifyPropertyChanged = Convert.ToBoolean(isNotifyPropertyChangedValue); } return isNotifyPropertyChanged; }
Das Ergebnis nach der Code-Generierung sieht wie folgt aus:
/// <summary> /// A sample model class. /// </summary> [DataContract] public class Class1 : INotifyPropertyChanged, INotifyPropertyChanged { private string attribute1; /// <summary> /// A Property that implements INotifyProeprtyChanged. /// </summary> [DataMember] public string Attribute1 { get{ return this.attribute1; } set{ if (this.attribute1 != value) { this.attribute1 = value; PropertyChanged(this, new PropertyChangedEventArgs("Attribute1")); } } } /// <summary> /// A normal property. /// </summary> public virtual string Attribute2 { get; set; } public event PropertyChangedEventHandler PropertyChanged; }
Der Code aus den Beispielen habe kann hier heruntergeladen werden.
Fazit
Die Modelgetriebene Entwicklung wird immer wichtiger und trägt einen wichtigen Teil zu wartbarem, sauberen und erweiterbarem Code bei. Visual Studio bringt mit den Modeling Projects und T4 mächtige Werkzeuge dafür mit. Allerdings ist die Dokumentation sehr dürftig und Out-Of-The-Box sind die Funktionen kaum verwendbar. Mit ein wenig Einarbeitung und eigenen Erweiterungen kann man aber doch in kurzer Zeit ein Lösung ohne den Einsatz von Drittherstellern bauen.