Share via


Grundlagen von „Oslo“

Erstellen metadatenbasierter Anwendungen mit der „Oslo“-Plattform

Chris Sells

Dieser Artikel basiert auf der Vorabversion der „Oslo“-Plattform. Änderungen an allen Informationen in diesem Artikel sind vorbehalten.

Themen in diesem Artikel:

  • „Oslo“ und Metadaten
  • MGraph und MSchema
  • SQL-Skripts, Verpacken und Bereitstellung
  • Visual Studio-Integration
In diesem Artikel werden folgende Technologien verwendet:
„Oslo“, SQL Server 2008

Inhalt

Metadaten
Aufbauen auf „Oslo“
MGraph und MSchema
SQL-Generierung
Verpacken und Bereitstellung
Visual Studio-Integration
Beispielcode

Eine auf Microsoft .NET Framework basierende Anwendung wird traditionell durch einen Satz kompilierten Codes und einen Satz zugehöriger Ressourcen definiert. Eine ASP.NET-Anwendung wird beispielsweise durch kompilierten Code (Assemblys) definiert, der die Logik Ihrer Website bereitstellt, sowie durch die Ressourcendateien (.aspx-Dateien), die die Seiten selbst definieren. Der Code wird (nach dem Kompilieren) von der CPU ausgeführt, und die Seiten werden von der ASP.NET-Laufzeit interpretiert. Ähnlich können Sie mittels Windows Workflow Foundation (WF) Verhaltensweisen als eine Reihe konfigurierter Aktivitäten definieren, bei denen es sich einfach um Daten handelt, die von der WF-Laufzeit interpretiert werden.

Wenn Sie ASP.NET auf der Benutzeroberflächenseite und WF auf der Verhaltensseite kombinieren, können Sie ganze Anwendungen fast vollständig als Daten erstellen, wobei Code lediglich verwendet wird, um Verhaltensweisen bereitzustellen, die von den Laufzeiten nicht geboten werden.

Die Daten, die diese Laufzeiten steuern, werden bisweilen als Metadaten bezeichnet. Metadaten können als die Daten definiert werden, die eine Anwendung beschreiben, beispielsweise, wie eine Homepage gerendert wird oder wie der erwerbende Workflow arbeitet. Ihre Anwendungsdaten stellen den Zustand der Ausführung einer Anwendung dar, etwa die Dinge, die ein Kunde auf Ihrer Website in seinem Warenkorb ablegt, oder seine Lieferadresse in Ihrem Kassenworkflow.

Um einen allgemeinen Satz an Tools zum Definieren von Metadaten bereitzustellen, damit sie an einem Ort gespeichert werden können, der denselben Satz an Features wie normale Anwendungsdaten bietet, erstellt Microsoft die Plattform „Oslo“, wobei es sich um eine Plattform zum Erstellen datengesteuerter Anwendungen handelt. „Oslo“ setzt sich aus drei Elementen zusammen: einer Familie von Sprachen, die zusammen als „M“ bezeichnet werden, einem Tool zur Bearbeitung visueller Daten namens „Quadrant“ und einem Datenspeicher, der als Repository bezeichnet wird.

In diesem Artikel werden einige Konzepte der „Oslo“-Plattform vorgestellt. Des Weiteren werden die Tools und Technologien eingeführt, die Sie benötigen, um diese Konzepte in die Praxis umzusetzen. Insbesondere wird erörtert, wie „Oslo“ Ihnen das Erstellen metadatengesteuerter Anwendungen mithilfe des MSchema- und des MGraph-Tools ermöglicht, und es wird auf die Grundlagen beim Definieren von Typen und Werten in „M“ sowie das Bereitstellen dieser Typen und Werte im Repository eingegangen.

„Oslo“: Eine neue Möglichkeit zum Implementieren einer alten Idee

Die Idee datengesteuerter Frameworks ist nichts Neues. Ziehen Sie ein anderes Beispiel in Betracht: Windows Presentation Foundation (WPF). Die meisten WPF-Anwendungen werden so geschrieben, dass ein großer Teil der Anwendung aus XAML besteht, das zur Laufzeit geladen und durch den XAML-Parser interpretiert wird. Dies sind die Metadaten, die die Definition einer WPF-Anwendung steuern. Sie können sogar die Definition einer WPF-Anwendung durch Laden oder Generieren von XAML, das dem Entwickler zur Kompilierzeit nicht bekannt war, zur Laufzeit ändern.

Das Bündeln von XAML (das in einer als BAML bezeichneten Form kompiliert wird) in einer Ressource zusammen mit Ihrem Anwendungscode ist jedoch nichts Besonderes. Es könnte genauso leicht in eine separate Datei oder sogar in eine relationale Datenbank geladen werden. Beim Laden in eine Datenbank würde das WPF-Ladeprogramm SQL-Abfragen durchführen, um auf Anwendungsdefinitionen zuzugreifen, statt Ressourcen zu laden, aber dem Benutzer wäre der Unterschied ansonsten nicht bewusst. Beim Laden einer Anwendungsdefinition aus einer Datenbank könnten alle diese Daten zentral aktualisiert werden, sodass der Entwickler der Anwendung die Möglichkeit erhält, Änderungen zu verbreiten, ohne die Anwendung erneut auf jedem Client bereitzustellen, der sie ausführen möchte. Dies ist im Grunde das beste Feature des Web, das für alle Arten von Anwendungen angestrebt werden sollte, unabhängig davon, ob sie im Browser ausgeführt werden oder nicht.

Sobald Sie nun eine Anwendung an einen zentralen Speicherort laden, ist es wünschenswert, dass Mehrbenutzerfeatures wie Replikation, Sicherheit und Versionsverwaltung vorhanden sind. Unterstützung für Benutzer, die getrennt wurden, und für Benutzer weltweit in verschiedenen Gebietsschemas sollte ebenfalls vorhanden sein.

Schließlich können Anwendungsdefinitionen für mehr als nur für das Steuern der Instanz einer Anwendung verwendet werden. Sie können zur Entwicklungszeit verwendet werden, um zu testen, ob bestimmte Teile einer Anwendung entsprechend den Spezifikationen ausgeführt werden. Sie können zur Laufzeit verwendet werden, um zu sehen, welche Teile verwendet werden oder Probleme haben. Sie können verwendet werden, um Probleme zu markieren, sodass die Entwickler genau sehen, was Schwierigkeiten verursacht. Wenn Sie über die Metadaten einer Anwendung verfügen, stehen Ihnen die gesamten leistungsfähigen Abfragefunktionen einer modernen, indizierten, abgestimmten, replizierten, gesicherten, versionierten, lokalisierten, offlinefähigen Datenbank zur Verfügung.

Das ist im Wesentlichen, worum es bei „Oslo“ geht: die Nutzung der Leistungsstärke von SQL Server zum Speichern der Metadaten einer Anwendung. Die Verwendung der Metadaten zum Steuern des Verhaltens einer Anwendung wird unter Windows mindestens seit der Zeit verwendet, als wir damit begannen, Binärdateien mit dem Konsolenbit zu markieren, um ein Konsolenfenster zu erhalten, ohne den Code zu schreiben. Mit „Oslo“ werden die Metadaten Ihrer Anwendung einfach formalisiert und verallgemeinert, und es werden dieselbe Leistungsstärke und dieselben Tools, die für die Daten Ihrer Anwendungen verwendet werden, bereitgestellt.

Metadaten

Metadaten unterscheiden sich von Ihren Anwendungsdaten dadurch, dass sie sich in einer Anwendung selten ändern. Sie ändern sich in der Regel selbst bei den wildesten Starts nur einmal pro Tag, wenn eine neue Version einer Website veröffentlicht wird. Andererseits kommt es oft zu recht umfangreichen Änderungen der Daten einer Anwendung, denn sie werden bei beliebten Websites von den Benutzern jeden Tag und den ganzen Tag über geändert.

Gehen wir noch einen Schritt weiter: Die Metadaten, die das Textverarbeitungsprogramm beschreiben, das ich zum Tippen dieses Artikels verwende, haben sich seit seiner Veröffentlichung im Jahr 2007 nicht geändert, aber die Daten haben sich allein heute beim Tippen von Zeichen und durch Bewegen der Maus zum Bearbeiten der Dokumente, an denen ich arbeite, viele tausend Mal geändert. Tatsächlich werden Metadaten so selten geschrieben, dass sie in vielen Fällen in schreibgeschützten Paketen bereitgestellt werden, wie beispielsweise die Klassendefinitionen in einer .NET-Assembly oder die vorkompilierten XAML-Dateien, die als Ressourcen in eine Assembly eingebettet sind. Da Entwickler ihre Anwendungen oft in Abhängigkeit von der statischen Natur von Metadaten schreiben (etwa die Ereignishandler für eine Schaltfläche, die auf einer ASP.NET-Seite definiert wird), ist das Ändern der Metadaten einer Anwendung fast immer unangebracht.

Folgendes ist jedoch an unserer derzeitigen Metadateninfrastruktur negativ: Die Tools zum Definieren und Lesen von Metadaten unterscheiden sich stark von jenen, die Sie für Anwendungsdaten verwenden würden.

Ich kann eine Anwendung zum Lesen und Schreiben von Daten über eine relationale Datenbank entwickeln, sodass mir ein leistungsfähiger Satz an Tools für Funktionen wie das Abfragen, Sichern, Replizieren und die Versionsverwaltung zur Verfügung steht. Wenn ich jedoch .NET Framework-Metadaten abfragen möchte, steht mir ein sehr viel begrenzterer Satz an Tools zur Verfügung, nämlich im Grunde nur die Reflection API.

Des Weiteren unterscheiden sich die .NET Framework-Tools zum Bearbeiten von Metadaten sehr stark von den Tools, die ich zum Analysieren von SQL-Metadaten verwende. Wenn ich Abfragen zwischen den Daten und Metadaten einer Anwendung durchführen möchte, d. h. Abfragen über die Workflows hinweg, die derzeit mit den Definitionen dieser Workflows ausgeführt werden, muss ich meine eigenen Tools erstellen, weil die Datenquellen andere sind: SQL statt XOML-Dateien (Extensible Object Markup Language).

Aufbauen auf „Oslo“

Es gibt viele Dinge, die Sie mit „Oslo“ erstellen können. Wenn wir intern von Szenarios sprechen, geht es in den Diskussionen im Endeffekt darum, Anwendungen zu erstellen, an denen Daten beteiligt sind. Ihnen wird jedoch aufgefallen sein, dass bei den Dingen, die ich als Teil von „Oslo“ aufgeführt habe, keine Laufzeiten erwähnt wurden. Tatsächlich sind .aspx-Dateien ohne ASP.NET nicht besonders nützlich, und XOML-Dateien währen ohne WF sehr viel weniger überzeugend.

Derzeit werden etliche Laufzeiten und Dienste entwickelt, die „Oslo“ nutzen. Dazu zählen eine neue Version von ASP.NET, die die domänenspezifische Sprache (DSL) „MWeb“ und den Webeditor „Quadrant“ unterstützt, die Windows Server-Erweiterungen „Dublin“, die die DSL „MService“ und den Diensteditor „Quadrant“ unterstützen, Entity Framework, das die DSL „MEntity“ und den Entitätseditor „Quadrant“ unterstützt, sowie SQL Server 2008 mit der MSchema-Sprache (auf die später in diesem Artikel eingegangen wird) und der Schemaeditor „Quadrant“. Sie werden in der Lage sein, Daten in das Repository zu laden und diese Laufzeiten auszuführen.

Das „Oslo“-Repository ist auf Anwendungen ausgerichtet, in denen Unternehmensentwickler eigene repositoryartige Elemente zum Speichern von Metadaten erstellt haben. „Oslo“ wäre beispielsweise eine gute Plattform für ein Repository für Computerkonfigurationsinformationen, die es den für den Betrieb verantwortlichen Mitarbeitern ermöglichen würden, Computerkonfigurationen problemlos abzufragen und zu verstehen, oder auch für ein Repository für Betriebsverfahren und -skripts. Da das Repository einen einheitlichen und abfragbaren Speicher für Metadaten bereitstellt, könnten Entwickler etwa folgende Fragen beantworten: „Welche Anwendungen und Klassen implementieren eine bestimmte Methode?“ und „Wirkt sich das Ändern dieser Methoden auf die Leistung aus?“

Ein weiterer potenzieller Bereich für den Einsatz von „Oslo“ könnte das Verstehen verteilter Anwendungen sein. Das Kombinieren von Profilerstellung, Codeabdeckung, Auslastungstests und architektonischen Daten innerhalb des Repositorys ermöglicht Entwicklern beispielsweise, Leistungsprobleme schnell zu lösen, indem sie die verwandte verteilte Komponente suchen, die Einstellungen verschiedener Konfigurationsparameter untersuchen und relevante Implementierungsinformationen anzeigen lassen. Außerdem wird die Entwicklung statischer Analysetools ermöglicht, die automatisch Entwurfsfehler melden.

Ein Bereich, der mir besonders gut gefällt, ist die Bereitstellung und Installation. Durch das Definieren von Setuppaketen in einer domänenspezifischen Sprache und das Laden der Pakete in das Repository erhält der Entwickler die Sprachdienste aller modernen Sprache zusammen mit Abfragetools für die Richtlinieneinhaltung. Sie könnten beispielsweise abfragen, ob Setups durch geeignete Lizenzvereinbarungen geschützt sind.

Ein Beispiel: Die Microsoft Download Center-Website stellt stark skalierbare Downloads für Kunden bereit. Damit Dateien auf dieser Website gehostet werden können, müssen sie in einer Microsoft Installer (MSI)-Datei verpackt und mit einem Endbenutzer-Lizenzvertrag (EULA) verbunden sein. Nachdem ich dies etliche Male mithilfe der grafischen Benutzeroberfläche in Visual Studio durchgeführt hatte, begab ich mich auf die Suche nach einer automatisierten Lösung. Ich stieß auf Windows Installer XML (WiX), mit dem ich XML zum Definieren und Kompilieren des Setup in einer MSI verwenden konnte.

In Abbildung 1 ist ein Beispiel für WiX-XML dargestellt. Für das ungeschulte Auge mag dies nicht wie eine Verbesserung gegenüber dem Ausführen einer grafischen Benutzeroberfläche aussehen, aber wenn Sie dieselben 27 Mausbewegungen etliche Male ausgeführt haben, wird der Programmierer in Ihnen die Oberhand gewinnen.

Abbildung 1 WiX-XML zum Erstellen einer MSI-Datei

<?xml version='1.0' encoding='Windows-1252'?>
<Wix xmlns="https://schemas.microsoft.com/wix/2006/wi">
  <Product Name="SuperNotepad" Id="d65d2125-560b-4ba9-bdca-3b3bcd3f7b70"
           UpgradeCode="ac5c9dac-b3f0-469a-8f1a-02c2566eee33" 
           Language="1033" Codepage="1252"
           Version="1.0.0.0" Manufacturer="SuperNotepad Corp.">
    <Package Description="A really super pad for notes" 
             Manufacturer="SuperNotepad Corp."
             InstallerVersion="200" Languages="1033" Compressed="yes" />
    <Media Id="1" Cabinet="setup.cab" EmbedCab="yes" />
    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="ProgramFilesFolder" Name="ProgramFilesFolder">
        <Directory Id="Directory3" Name="SuperNotepad Corp.">
          <Directory Id="INSTALLDIR" Name="Better NotePad">
            <Component Id="Component1" 
                       Guid="f7554ac8-32cd-44fb-bf85-0559c7d2e15c">
              <File Id="File1" Name="Manual.txt"
                    Source="C:\temp\SuperNotepad\Files\manual.txt" />
              <File Id="File2" Name="dependency.dll"
                    Source="C:\temp\SuperNotepad\Files\dependency.dll" />
              <File Id="File3" Name="BetterNotePad.exe"
                    Source="C:\temp\SuperNotepad\Files\notepad.exe" />
            </Component>
          </Directory>
        </Directory>
      </Directory>
    </Directory>
    <Feature Id="Feature1" Level="1">
      <ComponentRef Id="Component1" />
    </Feature>
  </Product>
</Wix>

MGraph und MSchema

Obwohl ich das Erstellen des WiX-XML zu einem gewissen Grad automatisieren kann, ist es nicht besonders sinnvoll, meine Setups in Unmengen von Größer-als- und Kleiner-als-Zeichen zu schreiben. Mit „Oslo“ kann ich sie in „M“ schreiben, einer Sprachfamilie, die MSchema, MGrammar und MGraph umfasst. (Die MSchema- und MGrammar-Spezifikationen können unter der „Oslo“-Modelliersprachspezifikation beziehungsweise unter der MGrammar-Sprachspezifikation eingesehen werden.)

Ich möchte MGraph und MSchema kurz vorstellen (in einem zukünftigen Artikel wird MGrammar näher untersucht werden). Wenn Sie zu Hause mitmachen möchten, empfehle ich Ihnen das Mr. Epl-Tool, das im „Oslo“-SDK enthalten ist. (Wir sprechen MREPL.EXE, was für M Read-Evaluate-Print-Loop steht, als „Mr. Epl“ aus.) Sie können es vom Texteditor „Intellipad“ des „Oslo“-SDK aus verwenden, indem Sie im Startmenü die Option „Intellipad (Samples Enabled)“ wählen, Strg+/ drücken, um den Minipuffer anzuzeigen, „SetMode('MScriptMode')“ (ohne die doppelten Anführungszeichen, aber unter Berücksichtigung der Groß-/Kleinschreibung) eingeben und die Eingabetaste drücken.

MGraph ist eine Sprache zum Definieren von Instanzen strukturierter Daten (auch als Werte bekannt). In diesem Beispiel habe ich drei Informationen definiert: eine anonyme ganze Zahl, eine anonyme Auflistung dreier ganzer Zahlen und einen Datensatz namens „pt1“ mit zwei Feldern, X und Y, bei denen es sich um ganze Zahlen handelt:

42
{ 1, 2, 3 }
pt1 { X = 10, Y = 20 }

MSchema ist eine Sprache zum Definieren von Einschränkungen für Daten. In MSchema ist ein Satz von Einschränkungen in einem Typ verbunden. Hier habe ich zwei benannte Typen definiert, nämlich einen SmallInteger-Typ, der den integrierten Integer32-Typ auf Werte unter 256 einschränkt, und einen Point-Entitätstyp (die Bezeichnung für Datensatztypen in M) mit zwei Feldern, X und Y:

type SmallInteger : Integer32 where value < 256;
type Point { X : Integer32; Y : Integer32; };

Sowohl X als auch Y sind auf Ganzzahlwerte zwischen -2.147.483.648 und 2.147.483.647 beschränkt.

Alle, die diesen Artikel lesen, sind bereits mit der CLR vertraut. Die CLR verwendet etwas, das als „nominelle Typisierung“ bezeichnet wird, wobei der einzelne Typ benannt wird, zu dem ein Wert gehört. „M“ andererseits basiert ähnlich wie XML auf struktureller Typisierung. In einer strukturell typisierten Welt kann ein Wert zu beliebig vielen Typen gehören, solange die Daten die von diesem Typ vorgegebenen Einschränkungen erfüllen. Um zu prüfen, ob ein Wert in dem Satz enthalten ist, der von einem Typ definiert wird, verwenden Sie den „in“-Operator (der hier im Mr. Epl-Tool angezeigt wird):

M>>> type SmallInteger : Integer32 where value < 256;
M>>> 4 in SmallInteger
true
M>>> 4 in Integer32
true
M>>> 256 in SmallInteger
false
M>>> 256 in Integer
true

Hier sehen Sie, dass 4 ein SmallInteger und ein Integer32 ist, während 256 nur ein Integer32 ist. Entitätswerte können ebenfalls auf Typmitgliedschaft geprüft werden:

M>>> type Point { X : Integer32; Y : Integer32; };
M>>> { X = 100, Y = 200 } in Point
true
M>>> pt1 { X = 10, Y = 20 }
M>>> pt1 in Point
true
M>>> pt1 in { X : SmallInteger; Y : SmallInteger; }
true
M>>> pt1 in { X; Y; }
true

Sie können sehen, dass sowohl das benannte als auch das unbenannte X,Y-Paar zum Point-Typ gehören und dass beide Felder des benannten X,Y-Paars SmallInteger-Werte sind und dieses Mal einen anonymen Typ definieren, der das Prüfen für Sie übernimmt. Die letzte Zeile zeigt an, dass das benannte X,Y-Paar zu dem Satz von Werten passt, die Felder namens X und Y haben, ohne dass ihre Werte eingeschränkt werden. Es könnte sich um Zeichenfolgen, Datumsangaben, Auflistungen oder irgendetwas anderes handeln.

Hier ein weiteres Beispiel. Da in diesem Fall das benannte X,Y-Paar nicht auch noch ein Z-Feld hat, passt es nicht zu dem anonymen Typ, für den ein X-, ein Y- und ein Z-Feld erforderlich sind:

M>>> pt1 in { X; Y; Z; }
false
M>>> pt1 in { x; y; }
false

Des Weiteren zeigt die letzte Zeile, dass bei M die Groß-/Kleinschreibung beachtet wird.

Schließlich sollten Sie den Standardwert und die Auflistungsschreibweisen verstehen:

type Product {
    Name : Text;
    ...
};

type Package {
    Product : Product; // required value
    Keywords : Text*; // collection of 0 or more values
    Description : Text?; // optional value
    Comments : Text?; // optional value
    Manufacturer : Text; // required value
    InstallerVersion : Integer32 = 200; // default value
    Language : Integer32 = 1033; // default value
    Compressed : Logical = true; // defaults value
};

Hier sehen Sie den Package-Typ aus dem WiX-Modul. Jedes Produkt (das Konzept oberster Ebene in einem MSI-Setup) verfügt über ein Paket, das über einen optionalen Satz an Textschlüsselwörtern, eine optionale Beschreibung und einen Kommentar, einen erforderlichen Hersteller, eine Installationsprogrammversion, die standardmäßig Windows Installer 2.0 ist, eine Sprache, die standardmäßig US-Englisch ist, und ein Komprimierungskennzeichen verfügt, das standardmäßig auf „true“ gesetzt ist.

Die erforderlichen Werte haben kein Suffix wie „Product“ oder „Manufacturer“. Die Standardwerte haben ein Gleichheitszeichen und einen Wert, beispielsweise „InstallerVersion“ und „Language“. Die optionalen Werte haben ein Fragezeichensuffix, beispielsweise „Description“ und „Comments“. Die Auflistungswerte (jene mit null oder mehr Werten) enden mit einem Sternchen. Das Fragezeichen und das Sternchen (? und *) sollen Sie an die Kleene-Operatoren Ihrer bevorzugten Sprache für reguläre Ausdrücke erinnern.

Hier als Beispiel ein Package-Wert:

Products { 
    SuperNotepad : Product* {
        Name = "SuperNotepad",
        ...
    }
}

Packages : Package* where item.Product in Products {
    Package1 {
        Product = Products.SuperNotepad,
        Description = "A really super pad for notes",
        Manufacturer = Products.SuperNotepad.Manufacturer,
        Keywords = { "Installer", "Super", "Notepad", "Sample" }
    }
}

Hier habe ich einen Teil eines Product und ein ganzes Package. Beachten Sie, dass zwei Arten von Einschränkungen vorhanden sind. Die erste Art hat die Form „: etwas“ wie in:

SuperNotepad : Product* {

Diese Typzuschreibung schränkt alle Werte innerhalb der geschweiften Klammern ein, um den Einschränkungen des Typs zu entsprechen.

Die zweite Einschränkung ist die Where-Klausel, die dem „M“-Compiler mitteilt, wo er den Speicher für die Product-Entitätswerte finden kann:

Packages : Package* where item.Product in Products {

Beachten Sie auch, dass zwei Werte Namen haben (SuperNotepad und Package1). Die MGraph-Syntax ermöglicht die Verwendung von Namen beim Initialisieren eines Wertediagramms, sodass ein Teil des Diagramms auf einen anderen Teil verweisen kann, ohne die Daten zu duplizieren. Dies geschieht, damit ich bei der Definition des Pakets auf mein Produkt verweisen kann.

Obwohl Mr. Epl sehr nützlich ist, sollten Sie für größere Aufgaben einen Texteditor verwenden. Sowohl Visual Studio 2008 als auch Intellipad stellen Sprachdienste bereit, sodass das Bearbeiten von „M“ zu einer angenehmen Erfahrung wird. Um beispielsweise zu prüfen, ob etwas syntaktisch gültig ist, können Sie über die roten Wellenlinien, die Fehler anzeigen (beispielsweise ein fehlendes doppeltes Anführungszeichen), nach Datentipps suchen.

Der gesamte „M“-Code muss in einem Modul vorhanden sein, ganz ähnlich wie der gesamte .NET-basierte Code in einem Namespace enthalten sein muss. Dies gibt den Bereich der Typen und Werte an.

Wenn Sie trotz des Mangels an Wellenlinien immer noch nicht zufrieden sind, können Sie den „M“-Compiler auch von der Befehlszeile aus ausführen:

C:\>set path=%path%;"c:\Program Files\Microsoft Oslo SDK 1.0\Bin"
...
C:\>m.exe /t:none wixbits.m
Microsoft (R) "Codename M" Compiler version 1.0.0925.0
Copyright (C) Microsoft Corporation. All rights reserved.
C:\>

Wenn vom „M“-Compiler keine Fehler gemeldet werden, ist Ihr Code sauber. Beachten Sie, dass ich nicht nur eine Datei mit einer .m-Erweiterung an m.exe übergebe, sondern (über den /t-Schalter) auch das Ziel „none“ angebe. Damit wird angegeben, dass nur die Syntaxüberprüfung durchgeführt werden soll. Ohne den Zielschalter wird der „M“-Compiler versuchen, Ihr Programm zum Generieren eines SQL-Skripts für das Erstellen von Tabellen und das Einfügen von Werten zu verwenden.

SQL-Generierung

Wenn Sie versuchen, SQL aus dem Beispielbit der WiX-Modelldaten zu generieren (nachdem das schließende doppelte Anführungszeichen hinzugefügt wurde), erhalten Sie einen Fehlerbericht wie diesen:

C:\>m wixbits.m
Microsoft (R) "Codename M" Compiler version 1.0.0925.0
Copyright (C) Microsoft Corporation. All rights reserved.

C:\wixbits.m(16,9): error M2010: The referenced type 'Product' does not define an identity constraint.
...

Sie erhalten diese Fehlermeldung auch, wenn Sie in Intellipad die Option „M Mode“ | „Reach SQL Preview“ wählen. Dabei geschieht Folgendes: Der „M“-Compiler versucht, ein SQL-Skript zu generieren, das den im Programm definierten Typen und Werten entspricht. Dies geschieht durch den Versuch, benannte Werte oberster Ebene (die als Blöcke bezeichnet werden) zuzuordnen, um Tabellenanweisungen zu erstellen und die Werte selbst Einfügeanweisungen zuzuordnen. Da „M“ jedoch die verschiedensten Verwendungen für allgemeine Datenbeschreibungen hat, wird nicht alles SQL-Konstrukten, beispielsweise Entitätstypen ohne Identitätsfelder, zugeordnet. Es handelt sich immer noch um gültiges „M“, das aber nicht für das Generieren von SQL gültig ist.

Das Generieren von SQL ist die Standardaktion des „M“-Compilers, da eine moderne relationale Datenbank eine hervorragende Umgebung für das Speichern und Ändern von Daten ist. Sie verfügt über alle möglichen wunderbaren Features wie Sicherheit, Replikation, Versionsverwaltung, Überwachung und so weiter. Wenn Sie SQL Server zum Hosten Ihrer Daten verwenden, erhalten Sie alle diese Features sowie eine Vielzahl von Optimierungen in Bezug auf Stabilität und Leistung.

Damit Ihre „M“-Typen mit SQL Server kompatibel sind, erfordert die Zuordnung eine Identitätsspalte, die Sie mithilfe des folgenden Codes hinzufügen können:

type Package {
    Id : Integer32 = AutoNumber();
    ...
} where identity Id;

Wie Sie sehen, erstelle ich ein Feld namens „Id“ und verwende die AutoNumber-Funktion zum Festlegen eines Standardwerts. Dann füge ich dem Typ mithilfe des where-Schlüsselworts, das das Id-Feld als Identitätsspalte markiert, eine Einschränkung hinzu. Wenn diese vorhanden ist, habe ich genug Daten, um gültiges SQL für den Typ abzurufen, wie in Abbildung 2 dargestellt.

Abbildung 2 Von „M“ generiertes SQL

...
create table [WixBits].[Packages](
  [Id] int not null identity,
  [Comments] nvarchar(max) null,
  [Compressed] bit not null default 1,
  [Description] nvarchar(max) null,
  [InstallerVersion] int not null default 200,
  [Language] int not null default 1033,
  [Manufacturer] nvarchar(max) not null,
  [Product] int not null,
  constraint [PK_Packages] primary key clustered ([Id]),
  constraint [FK_Packages_Product_WixBits_Products] foreign key ([Product]) references [WixBits].[Products] ([Id])
);
...

create table [WixBits].[Packages_Keywords](
  [_Id] bigint not null identity,
  [Packages_Id] int not null,
  [Item] nvarchar(max) not null,
  constraint [PK_Packages_Keywords] primary key clustered ([_Id]),
  constraint [FK_Packages_Keywords_Packages_Id_WixBits_Packages] foreign key 
  ([Packages_Id]) references [WixBits].[Packages] ([Id]) on delete cascade
);
...

Hoffentlich entspricht das vom „M“-Compiler generierte SQL dem, was Sie selbst schreiben würden. Modulnamen sind SQL-Schemanamen zugeordnet. Typnamen sind Tabellennamen zugeordnet. AutoNumber ist „identity“ im Feld zugeordnet, und die Identitätseinschränkung ist der Einschränkung des Primärschlüssels im Feld zugeordnet. Interne „M“-Typen wie „Integer32“ und „Text“ werden entsprechenden SQL-Typen wie „int“ und „nvarchar“ zugeordnet. Auflistungsfelder (das Keywords-Feld des Typs „Text*“) sind einer anderen Tabelle zugeordnet, die null oder mehr Elemente in dieser Auflistung mit der entsprechenden Fremdschlüsseleinschränkung enthält. Abbildung 3 zeigt, wie die Werte zugeordnet werden.

Abbildung 3 Zuordnen von „M“-Typen zu SQL

insert into [WixBits].[Packages] ([Product], [Description], [Manufacturer])
 values (@WixBits_Products_Id0, N'A really super pad for notes', @WixBits_Products_Manufacturer0);
declare @WixBits_Packages_Id0 bigint = @@identity;

insert into [WixBits].[Packages_Keywords] ([Item], [Packages_Id])
 values (N'Installer', @WixBits_Packages_Id0);

insert into [WixBits].[Packages_Keywords] ([Item], [Packages_Id])
 values (N'Super', @WixBits_Packages_Id0);

insert into [WixBits].[Packages_Keywords] ([Item], [Packages_Id])
 values (N'Notepad', @WixBits_Packages_Id0);

insert into [WixBits].[Packages_Keywords] ([Item], [Packages_Id])
 values (N'Sample', @WixBits_Packages_Id0);

Verpacken und Bereitstellung

Die Standardausgabe des „M“-Compilers ist SQL, das Sie wahrscheinlich an eine SQL Server 2008-Instanz übertragen möchten. Standardmäßig ist dieses SQL als einzelne SQL-Skriptdatei verpackt:

C:\>m.exe wixbits.m
Microsoft (R) "Codename M" Compiler version 1.0.0925.0
Copyright (C) Microsoft Corporation. All rights reserved.

C:\>dir wixbits.sql
...
11/22/2008  04:43 PM             2,545 wixbits.sql
...

Sie können diese Skriptdatei ganz nach Belieben zum Auffüllen Ihrer SQL-Datenbankinstanz verwenden, beispielsweise mit sqlcmd.exe oder SQL Server Management Studio. Das SQL-Skript enthält jedoch viel weniger Metadaten als der ursprüngliche „M“-Quellcode. Wenn Sie diese Metadaten verwalten möchten (und das könnte angebracht sein, wie ich gleich erklären werde), können Sie das „M“-Bildverpackungsformat mit dem /p:image-Befehlszeilenschalter für m.exe wählen:

C:\>m.exe /p:image wixbits.m
Microsoft (R) "Codename M" Compiler version 1.0.0925.0
Copyright (C) Microsoft Corporation. All rights reserved.
C:\>dir wixbits.mx
...
11/22/2008  04:44 PM             9,073 wixbits.mx
...

Die wixbits.mx-Datei ist eine „M“-Bilddatei, die das generierte SQL sowie ein Manifest und andere Metadaten enthält. Da die Datei das OPC-Format (Open Packaging Conventions) besitzt, können Sie eine .zip-Dateierweiterung anhängen und die Datei wie jede andere .zip-Datei öffnen. Wichtiger ist jedoch, dass Sie eine .mx-Datei für Verweise und zur Bereitstellung verwenden können.

Wenn Sie eine .NET-Assembly erstellen, können Sie als Teil Ihres Erstellungsprozesses auf diese Assembly verweisen, um öffentliche Typen und statische Instanzen in Ihre .NET-basierten Programme zu ziehen. Wenn Sie den C#- oder Visual Basic-Compiler über die Befehlszeile verwenden, können Sie mit dem /r-Schalter („reference“) auf vorhandene .NET-Assemblys verweisen. Ähnlich können Sie beim Kompilieren Ihrer „M“-Programme den /r-Schalter im „M“-Compiler verwenden, um andere Typen aus „M“-Bildern zu ziehen. Damit dies funktioniert, müssen Sie zuerst Ihre Typen oder Blöcke aus dem Modul exportieren, auf das Sie verweisen:

// WixbitsTypes.m 
module WixBits {
    export Product, Package; // export types
    export Products, Packages; // export extents

    type Product {...};
    type Package {...};
    Products : Product*;
    Packages : Package* where item.Product in Products;
}

Dann können Sie Typen und Blöcke, die exportiert wurden, importieren:

// WixbitsValues.m
module WixBits {
    import WixBits; // bring in exported types and extents
    ...
}

Nachdem Sie eine „M“-Bilddatei mit Exporten erstellt haben, können Sie darauf verweisen, wenn Sie „M“-Programme mit Importen kompilieren (weitere Informationen finden Sie in Abbildung 4).

Abbildung 4 Kompilieren mit importierten Typen und Blöcken

C:\>m.exe /p:image /t:Repository WixbitsTypes.m
Microsoft (R) "Codename M" Compiler version 1.0.0925.0
Copyright (C) Microsoft Corporation. All rights reserved.

C:\>dir WixbitsTypes.mx
...
11/22/2008  05:48 PM            28,332 WixbitsTypes.mx
...

C:\>m.exe /p:image /t:Repository /r:WixbitsTypes.mx WixbitsValues.m
Microsoft (R) "Codename M" Compiler version 1.0.0925.0
Copyright (C) Microsoft Corporation. All rights reserved.

C:\>dir WixbitsValues.mx
...
11/22/2008  05:50 PM             5,606 WixbitsValues.mx

Neben dem Verpacken von Typen und Blöcken für Verweise in anderen „M“-Programmen kann eine „M“-Bilddatei in einer SQL Server-Datenbankinstanz folgendermaßen mithilfe des „M“-Ladedienstprogramms „mx.exe“ bereitgestellt werden:

C:\>mx.exe /db:Repository /i:WixbitsTypes.mx /i:WixbitsValues.mx
Microsoft (R) "Codename M" Command-line Utility version 1.0.0925.0
Copyright (C) Microsoft Corporation. All rights reserved.

Hier verwende ich den /db-Schalter, um anzugeben, zu welcher Datenbank eine Verbindung hergestellt werden soll, wenn angenommen wird, dass der lokale Host als Datenbankserver dient, und die beiden Bilddateien installiert werden. Ich lade die Tabellen in SQL und fülle sie mit meinen Werten auf, was bedeutet, dass ich die Datenzugriffstechnologie meiner Wahl verwenden kann, um sie wieder zurückzuerhalten (oder zu aktualisieren, zu löschen oder neue einzufügen). In diesem Fall verwende ich das sqlcmd-Befehlszeilentool:

C:\>sqlcmd -d Repository
1> select Name from WixBitx.Products
2> go
Name
----------------
SuperNotepad
(1 rows affected)

Obwohl Intellipad standardmäßig „M“-Sprachdienste einschließlich SQL-Generierung unterstützt, ist kein unterstützter Mechanismus für das Erstellen des Bilds oder das Verweisen auf andere Bilder vorhanden. Visual Studio unterstützt diese Features jedoch.

Visual Studio-Integration

Das „Oslo“-SDK wird mit integrierter Unterstützung für Visual Studio 2008 SP1 geliefert. Nach Installation des SDK ist die „M“ Project-Vorlage installiert, wie in Abbildung 5 dargestellt.

Standardmäßig wird Ihrem Projekt eine Model1.m-Datei hinzugefügt. Sie bietet dieselben IntelliSense-Features, die in Intellipad vorhanden sind. Wenn Sie mehr Dateien modellieren müssen, können Sie mit der rechten Maustaste im Projektmappen-Explorer auf Ihr Projekt klicken, ein neues Element hinzufügen und dann die „M“ Model-Vorlage auswählen.

Abbildung 5 Die Vorlage '“M” Project' in Visual Studio 2008

Wenn Sie auf vorhandene „M“-Bilder verweisen möchten, klicken Sie mit der rechten Maustaste auf Ihr Projekt, und wählen Sie zunächst „Add Reference“ (Verweis hinzufügen) und dann eine .mx-Datei aus. Wenn Sie beispielsweise ein Projekt erstellen möchten, das die von mir verwendeten Werte erstellt und auf die Typen verweist, die ich vorgestellt habe, wird es wie in Abbildung 6 aussehen.

Abbildung 6 Ein „M“-Projekt mit Quellcode und Verweis auf .mx-Datei

Beim Erstellen sehen Sie die Fehler wie erwartet in der Error List, und die Ausgabe wird (in Abhängigkeit von der Konfiguration, die Sie erstellen) in bin\Debug oder bin\Release angezeigt, wobei sowohl die .sql- als auch die .mx-Datei enthalten sind, mit denen Sie nach Wunsch verfahren können (indem Sie beispielsweise mx.exe für die .mx-Ausgabedatei ausführen). Alle diese Dinge funktionieren auch im aktuellen CTP von Visual Studio 2010.

Der Erstellungsprozesses in Visual Studio wird von einer MSBuild-Datei mit der .mproj-Erweiterung gesteuert (siehe Abbildung 7). Dies ist praktisch, wenn Sie die „Oslo“-msbuild-Zieldatei in Ihren eigenen Projektdateien verwenden möchten.

Abbildung 7 MSBuild-Datei für „M“-Projekte

<?xml version="1.0" encoding="utf-8"?>
<Project ...>
  <PropertyGroup>
    <MTarget>Repository</MTarget>
    <MPackageScript>true</MPackageScript>
    <MPackageImage>true</MPackageImage>
    <MTargetsPath Condition="$(MTargetsPath) == ''">
      $(MSBuildExtensionsPath)\Microsoft\M\v1.0</MTargetsPath>
    ...
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="MixbitsValues.m" />
  </ItemGroup>
  <ItemGroup>
    <MReference Include="C:\WixbitsTypes.mx" />
  </ItemGroup>
  <Import Project="$(MTargetsPath)\MProject.targets" />
</Project>

Beispielcode

Der zu diesem Artikel gehörende Beispielcode ist ein einfaches Repository Installer-Dienstprogramm (repinst.exe), das ein Modell mit ein paar mehr Features zum Beschreiben der MSI-Dateien versteht, die hier erörtert wurden. Nachdem die Typen und Werte, die die WiX-Modelle beschreiben, (mithilfe von m.exe) kompiliert und dann (mithilfe von mx.exe) in das Repository geladen wurden, werden durch Ausführen des repinst.exe-Dienstprogramms für die Wertdaten die Daten aus dem Repository gelesen, aus ihnen wird eine MSI-Datei erstellt, und das wird Setup gestartet, sodass alle, die Zugriff auf die Datenbank haben, die auf diese Weise geladenen Anwendungen bereitstellen können, wie in Abbildung 8 dargestellt.

fig08.gif

Abbildung 8 Das repinst-Beispiel in Aktion

Ich habe ein wenig an diesem Beispiel gearbeitet und es hauptsächlich zu Darstellungszwecken modifiziert, aber der Hauptteil der Arbeit wurde von Thomas Delrue, Alejandro Trigo und Giovanni Della-Libera beim Modellieren von WiX, Übersetzen von „M“ in WiX und Herstellen einer MSI-Datei aus SQL-Daten geleistet, wofür ihnen die Anerkennung gebührt. Ich hätte diesen Artikel ohne ihre bahnbrechende Arbeit nicht in der verfügbaren Zeit schreiben können. Mein Dank geht auch an Josh Williams, Martin Gudgin und John Doty für ein ganz anderes Beispiel, das erstellt wurde, um die Features des Repository zu untersuchen. Sie werden es bald im „Oslo“-Entwicklercenter sehen.

Chris Sells ist arbeitet als Programmmanager für die Connected Systems Division bei Microsoft. Dort befasst er sich mit Anwendungsentwicklungsverfahren der nächsten Generation. Weitere Informationen über Chris Sells und seine verschiedenen Projekte finden Sie unter sellsbrothers.com.