Share via

February 2009

Volume 24 Number 02

"Oslo" Basics - Build Metadata-Based Applications With The "Oslo" Platform

By Chris Sells | February 2009

Code download available at:OsloIntro2009_02.exe(165 KB)
Browse the Code Online

This article is based on a prerelease version of the "Oslo" platform. All information is subject to change.

This article discusses:

  • ”Oslo” and metadata
  • MGraph and MSchema
  • SQL scripts, packaging, and deployment
  • Visual Studio integration
This article uses the following technologies:
”Oslo,” SQL Server 2008


Building on "Oslo"
MGraph and MSchema
SQL Generation
Packaging and Deployment
Visual Studio Integration
Sample Code

An application based onthe Microsoft .NET Framework is traditionally defined by a set of compiled code and a set of associated resources. For example, an ASP.NET application is defined by compiled code (assemblies) that provide the logic of your Web site and the resource files (.aspx files) that define the pages themselves. The code is executed by the CPU (after compilation) and the pages are interpreted by the ASP.NET runtime. Similarly, Windows Workflow Foundation (WF) lets you define behaviors as a series of configured activities, all of which is just data to be interpreted by the WF runtime.

Combine ASP.NET on the UI side and WF on the behavior side and you can start to imagine building entire applications as almost completely data, using code only to fill in behaviors not provided by the runtimes.

The data that drives these runtimes is sometimes called metadata. Metadata can be defined as the data that describes an application, for example, how a home page is rendered or how the purchasing workflow operates. Your application data represents the state of the execution of an application, such as what Bob put in his shopping cart on your Web site or his shipping address in your checkout workflow.

To provide a common set of tools for defining metadata so that it can be stored in a place that provides the same set of features as normal application data, Microsoft is creating "Oslo," a platform for building data-driven applications. "Oslo" is composed of three elements: a family of languages collectively called "M," a visual data manipulation tool called "Quadrant," and a data store called the repository.

In this article, I will introduce you to some of the concepts of the "Oslo" platform. I will also introduce you to the tools and technologies you need to employ to put those concepts into practice. In particular, I will discuss how "Oslo" enables you to build metadata-driven apps, using the MSchema and MGraph tools, and the basics of defining types and values in "M" as well as deploying them to the repository.

“Olso”: A New Way to Implement an Old Idea

The idea ofdata-driven frameworks is nothing new. Consider another example: Windows Presentation Foundation (WPF). The way most WPF applications are written, much of the application is composed of XAML, which is loaded at run time and interpreted by the XAML parser. This is the metadata that drives the definition of a WPF application. You can even change the definition of a WPF application by loading or generating XAML at run time that the developer didn't know about at compile time.

However, there's nothing magical about the XAML (compiled into a form known as BAML) being bundled into a resource with your application code—it could as easily be loaded into a separate file or even into a relational database. If it were loaded into a database, the WPF loader would perform SQL queries to access application definitions instead of loading resources, but the user would otherwise be unaware of the difference. And if an application definition would loaded from a database, all of that data could be updated centrally, giving the developers of the application the ability to propagate changes without re-deploying the application to every client that wanted to run it. This is, in fact, the killer feature of the Web and one worth driving towards for all kinds of applications, whether they run in the browser or not.

Now, as soon as you load an application into a central location, you're going to want multi-user features such as replication, security, and versioning. You'll also want support for disconnected users and for users in different locales around the world.

And finally, application definitions can be used for more than just driving an instance of an application. They can be used at development time to test whether particular parts of an application are running according to specifications. They can be used at run time to show what parts are getting usage or having trouble. They can be used to tag issues so developers can see specifically what's causing the trouble. Having an application's metadata provides all the query power of a modern, indexed, tuned, replicated, secured, versioned, localized, offline-capable database.

And that's essentially what "Oslo" is all about—using the power of SQL Server to store an application's metadata. Using the metadata to drive the behavior of an application has been used on Windows at least since we started tagging binaries with the console bit to get a console window without writing the code. "Oslo" is simply about formalizing and generalizing the metadata of your application and providing the same power and tools used for the data of your applications.


What makes metadata distinct from your application data is that the metadata for an application seldom changes—it's typically something that even in the wildest startups only changes once a day when a new version of a Web site is pushed live. On the other hand, the data for an application often changes quite a lot—it's what users are manipulating all day, every day on a popular Web site.

Taking this further, the metadata that describes the word processor I'm using to type this article hasn't changed since 2007 when it was released, but the data has changed thousands of times just today as I type characters and move the mouse to manipulate the documents I'm working on. In fact, metadata is written so seldom that in many cases it's delivered in read-only packages, like the class definitions in a .NET assembly or the precompiled XAML files embedded as resources into an assembly. Since developers often write their applications depending on the static nature of metadata, like the event handlers for a button defined in an ASP.NET page, changing an application's metadata seldomly isn't a bad thing.

However, the thing that is kind of bad about our current metadata infrastructure is that the tools for defining and reading metadata are so different from those you would use for application data.

I can develop an application to read and write data via a relational database, giving me a powerful set of tools for things like querying, securing, replicating, and versioning. But if I want to query .NET Framework metadata, I have a much more limited set of tools: the Reflection API, basically.

Further, the .NET Framework tools for manipulating metadata are very different from the tools I use to crack open SQL metadata. If I want to query between an application's data and metadata—query across the workflows currently being executed with the definitions of those workflows—I've got to build my own tools because the data sources are different: SQL versus Extensible Object Markup Language (XOML) files.

Building on "Oslo"

There are many things that you can build with "Oslo." When we talk about scenarios internally, the discussion often boils down to building applications that involve data. However, you'll notice that in the set of things that I listed as part of "Oslo" I didn't mention any runtimes. In fact, .aspx files aren't too useful without ASP.NET, and XOML files without WF would be much less compelling.

There are a number of runtimes and services being developed that will take advantage of "Oslo." They include domain-specific languages (DSLs) and Quadrant designers for for ASP.Net Web applications, WCF Web Services, and EF Databases. You'll be able to load data into the repository and drive those runtimes.

The "Oslo" repository is geared toward apps where enterprise developers have built their own repository-like things for storing metadata. For example, "Oslo" would be a good platform for a repository of machine configuration information that would allow operations staff to query and understand machine configurations easily, or similarly, a repository of operational procedures and scripts. For developers, because the repository provides a unified and queriable store for metadata, it would be possible to answer questions such as "Which applications and classes implement a certain method?" and "Will changing this method impact performance?"

Another potential area for employing "Oslo" could be for understanding distributed applications. Combining profiling, code coverage, load testing, and architectural data within the repository will allow developers to, for example, quickly resolve performance issues by browsing to the related distributed component, examining the settings of various configuration parameters, and viewing relevant implementation details. In addition, it will allow for the development of static analysis tools that automatically report design defects.

One area that particularly tickles my fancy is deployment and installation. By defining setup packages in a domain-specific language and loading them into the repository, a developer gets the language services of any modern language along with query tools for policy compliance. For example, you could query whether setups are protected with appropriate license agreements.

Here's an example: the Microsoft download center Web site provides for highly scalable downloads for customers. For files to be hosted on that site, they must be packaged in a Microsoft Installer (MSI) file and tagged with an end-user license agreement (EULA). After doing this about half a dozen times using the GUI in Visual Studio, I went looking for an automated solution and found one in Windows Installer XML (WiX), which let me use XML to define the setup and compile it into an MSI.

Figure 1shows an example of the WiX XML. To the untrained eye, this might not seem like an improvement over running a graphical UI, but once you've done the same 27 mouse gestures a few times, the programmer in you will take over.

Figure 1 WiX XML to Create an MSI File

<?xml version='1.0' encoding='Windows-1252'?> <Wix xmlns=""> <Product Name="SuperNotepad" Id="d65d2125-560b-4ba9-bdca-3b3bcd3f7b70" UpgradeCode="ac5c9dac-b3f0-469a-8f1a-02c2566eee33" Language="1033" Codepage="1252" Version="" Manufacturer="SuperNotepad Corp."> <Package Description="A really super pad for notes" Manufacturer="SuperNotepad Corp." InstallerVersion="200"Languages="1033" Compressed="yes" /> <Media Id="1" Cabinet="" EmbedCab="yes" /> <DirectoryId="TARGETDIR" Name="SourceDir"> <Directory Id="ProgramFilesFolder" Name="ProgramFilesFolder"> <DirectoryId="Directory3" Name="SuperNotepad Corp."> <Directory Id="INSTALLDIR" Name="Better NotePad"> <ComponentId="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 and MSchema

Even though I can automate creation of the WiX XML to some extent, I don't really want to write my setups in a sea of angle brackets. "Oslo" lets me write them in "M", a family of languages including MSchema, MGrammar, and MGraph. (The MSchema and M­Grammar specifications can be found at " The "Oslo" Modeling Language Specification" and " MGrammar Language Specification," respectively.)

Let me give you a quick intro to MGraph and MSchema (I'll explore MGrammar in a future article). If you'd like to play along at home, I highly recommend the Mr. Epl tool that comes with the "Oslo" SDK. ("Mr. Epl" is how we pronounce MREPL.EXE, which stands for M Read-Evaluate-Print-Loop.) You can use it from within the text editor called Intellipad that ships in the "Oslo" SDK by choosing Intellipad (Samples Enabled) from the Start menu, pressing Ctrl+/ to bring up the mini-buffer, and typing "SetMode('MScriptMode')" (no quotes, but case matters), followed by pressing Enter.

MGraph is a language for defining instances of structured data (better known as values). In this example, I've defined three pieces of data: an anonymous integer, an anonymous collection of three integers, and a record named "pt1" with two fields, X and Y, both integers:

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

MSchema is a language for defining constraints over data. In MSchema, a set of constraints is tied together into a type. Here I've defined two named types, a SmallInteger type that constrains the built in Integer32 type to only those values less than 256, and a Point entity type (the name for record types in M) with two fields, X and Y:

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

Both X and Y are constrained to hold integer values between -2,147,483,648 and 2,147,483,647.

Anyone reading this article is already familiar with the CLR. The CLR uses something called "nominal typing," that is, naming the single type to which a value belongs. "M," on the other hand, is based on structural typing, much like XML. In a structurally typed world, a value can belong to any number of types so long as the data meets the constraints set by that type. To check whether a value is in the set defined by a type, you use the "in" operator (shown here from inside the Mr. Epl tool):

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

Here you can see that 4 is a SmallInteger and an Integer32, but 256 is only an Integer32. Entity values can be checked for type membership as well:

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

You can see that the named and unnamed X, Y pairs are both Points and that both fields of the named X, Y pair are SmallInteger values, this time defining an anonymous type to do the checking for you. The last line indicates that the named X, Y pair fits into the set of values that have fields named X and Y, with no restriction on their values—they could be strings or dates or collections or whatever.

Here's another example. In this case, because the named X, Y pair doesn't also have a Z field, it doesn't fit into the anonymous type that requires X, Y, and Z fields:

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

Further, the last line shows that M is case-sensitive.

Finally, you'll want to understand the default value and collection notations:

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 };

Here you see the Package type from the WiX module. Every product (the top-level concept in an MSI setup) has a package, which has an optional set of text keywords, an optional description and comment, a required manufacturer, an installer version that defaults to Windows Installer 2.0, a language the defaults to U.S. English and a compression flag that defaults to true.

The required values have no suffix, like Product and Manufacturer. The default values have an equals sign and a value, such as InstallerVersion and Language. The optional values have a question mark suffix, such as Description and Comments. The collection values (those with zero or more values) end in star. The question mark and star notions (? and *) are meant to remind you of the Kleene operators from your favorite regular expression language.

Here's an example Package value:

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" } } }

Here I've got part of a Product and an entire Package. Notice that I have two kinds of constraints. The first kind is of the form ": something", as in:

SuperNotepad : Product* {

This is called type ascription and constrains all values inside the curly braces to conform to the constraints of the type.

The second constraint is the where clause that tells the "M" compiler where to find the storage for the Product entity values:

Packages : Package* where item.Product in Products {

Also notice that two of the values have names (SuperNotepad and Package1). The MGraph syntax allows for names to be used in the middle of initializing a graph of values so that one part of the graph can reference another part without duplicating the data. I am doing this so that I can reference my product in the definition of the package.

As useful as Mr. Epl is, you'll want to use a text editor for larger things. Both Visual Studio 2008 and Intellipad provide language services to make editing "M" a pleasant experience. For example, to check that you've got something syntactically valid, you can look for data tips over the red squiggles showing errors (for example, a missing closing double quote).

All "M" code must be inside a module, much like all .NET-based code must be inside a namespace. This scopes the types and values.

If the lack of squiggles isn't enough to make you happy, you can also run the "M" compiler from the command line:

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:\>

If there are no errors reported by the "M" compiler, your code is clean. Notice here that in addition to passing a file with a .m extension to m.exe, I am also specifying a target (via the /t switch) of none. This means to just do the syntax checking. Without the target switch, the "M" compiler will attempt to use your program to generate a SQL script to create tables and insert values.

SQL Generation

If you try to generate SQL from the sample bit of WiX model data (after adding the closing double quote), you'll get an error report like this one:

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. ...

You can also get this error message if you choose M Mode | Reach SQL Preview from inside Intellipad. What's happening is that the "M" compiler is trying to generate a SQL script that matches the types and values defined in the program. It does so by attempting to map top-level named values (called extents) to create table statements and to map the values themselves into insert statements. However, because "M" has all kinds of general-purpose data description uses, not everything in it maps to SQL constructs—for example, entity types without identity fields. It's still valid "M," but not valid for generating SQL.

Generating SQL is the default action of the "M" compiler because a modern relational database is a fabulous environment for storing and manipulating data. It has all kinds of wonderful features such as security, replication, versioning, auditing, and on and on. If you make use of SQL Server for hosting your data, you get all of those features, plus a host of robustness and performance optimizations.

To make your "M" types compatible with SQL Server, the mapping requires an identity column, which you can add using the following code:

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

As you can see, I am creating a field named Id and using the AutoNumber function to set a default value. Then, I add a constraint on the type using the where keyword that marks the Id field as the identity column. With this in place, I have enough data to get valid SQL for the type, as shown in Figure 2.

Figure 2 SQL Generated by "M"

... create table [WixBits].[Packages]( [Id] int not null identity, [Comments] nvarchar(max) null, [Compressed] bit notnull default 1, [Description] nvarchar(max) null, [InstallerVersion] int not null default 200, [Language] int not nulldefault 1033, [Manufacturer] nvarchar(max) not null, [Product] int not null, constraint [PK_Packages] primary keyclustered ([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] intnot 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 ); ...

Hopefully the SQL generated by the "M" compiler matches what you'd write yourself. Module names map to SQL schema names. Type names map to table names. AutoNumber maps to "identity" on the field, and the identity constraint maps to the primary key constraint on the field. Intrinsic "M" types such as Integer32 and Text map go corresponding SQL types such as int and nvarchar. Collection fields—the Keywords field of type Text*—maps to another table to hold the zero or more items in that collection, with the corresponding foreign key constraint. Figure 3illustrates how the values map.

Figure 3 Mapping "M" Types to SQL

insert into [WixBits].[Packages] ([Product], [Description], [Manufacturer]) values (@WixBits_Products_Id0, N'Areally 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);

Packaging and Deployment

The default output of the "M" compiler is SQL, which you'll likely want to push into a SQL Server 2008 instance. By default, that SQL is packaged as a single SQL script file:

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 ...

You are free to use this script file to populate your SQL database instance however you like, such as with sqlcmd.exe or SQL Server Management Studio. However, the SQL script has a lot less of the metadata than the original "M" source code. If you'd like to maintain that metadata—and there is value in doing so, as I will explain in a moment—you can choose the "M" image packaging format with the /p:image command line switch to m.exe:

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 ... 11/22/2008 04:44 PM 9,073 ...

The file is an "M" image file, which contains the generated SQL as well as a manifest and other metadata. Since the file is in OPC (Open Packaging Conventions) format, you can stick a .zip file extension on it and open it just like any other .zip file. More importantly, however, you can use a .mx file for references and for deployment.

When you build a .NET assembly, you can reference that assembly as part of your build process to pull public types and static instances into your .NET-based programs. If you're using the C# or Visual Basic compilers from the command line, you can reference existing .NET assemblies with the /r (reference) switch. Likewise, if you're compiling your "M" programs, you can employ the /r switch on the "M" compiler to pull in other types from "M" images. For that to work, you must first export your types or extents from the module you're referencing:

// 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; }

You can then import types and extents that have been exported:

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

Once you've built an "M" image file with exports, you can reference that when compiling "M" programs with imports (for more information, see Figure 4).

Figure 4 Compiling with Imported Types and Extents

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 ... 11/22/2008 05:48 PM 28, ... C:\>m.exe /p:image /t:Repository / WixbitsValues.m Microsoft (R)"Codename M" Compiler version 1.0.0925.0 Copyright (C) Microsoft Corporation. All rights reserved. C:\> ... 11/22/2008 05:50 PM 5,606

In addition to packaging up types and extents for reference in other "M" programs, an "M" image file can be deployed to a SQL Server database instance using the "M" loader utility, mx.exe, in the following manner:

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

Here I am using the /db switch to specify which database to connect to, assuming the local host as the database server and installing the two image files. I load the tables into SQL and populate them with my values, which means I can use the data access technology of my choice to pull them back out again (or update them or delete them or insert new ones). In this case, I'm using the sqlcmd command-line tool:

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

While Intellipad supports "M" language services out of the box, including generating SQL, there is no supported mechanism for building the image or referencing other images. However, Visual Studio supports these features.

Visual Studio Integration

The "Oslo" SDK ships with built-in support for Visual Studio 2008 SP1. After you install the SDK, you will have the "M" Project template installed, as shown in Figure 5.

By default, you'll get a Model1.m file added to your project. This provides the same IntelliSense features you got in Intellipad. When you need more model files, you can right-click on your project in the Solution Explorer, add a new item, and then choose the "M" Model template.

Figure 5 The “M” Project Template in Visual Studio 2008

Further, if you'd like to reference existing "M" images, you can do so by right-clicking on your project and choosing Add Reference, then choosing a .mx file. For example, if you'd like to create a project that builds the values I've been playing with and references the types I've been demonstrating, it will look like Figure 6.

Figure 6 An “M” Project with Source Code and .mx File Reference

When you build, you'll see the errors in the Error List as you'd expect and the output will go into \bin\Debug or \bin\Release (depending on the configuration that you're building), including both the .sql and .mx files for you to do with as you see fit (like run mx.exe on the output .mx file). All of these same things also work in the latest CTP of Visual Studio 2010.

The build process inside Visual Studio is driven by an MSBuild file with the .mproj extension (see Figure 7). This is handy if you'd like to use the "Oslo" msbuild targets file in your own project files.

Figure 7 MSBuild File for "M" Projects

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

Sample Code

The sample code included with this article is a toy Repository Installer utility (repinst.exe) that understands a slightly more full-featured model for describing MSI files that I've been discussing here. Once the types and values that describe the WiX models have been compiled (using m.exe) and loaded into the repository (using mx.exe), running the repinst.exe utility on the value data will read the data from the repository, create an MSI file from it, and launch the setup, allowing anyone with access to the database to deploy applications thus loaded, as shown in Figure 8.


Figure 8 The repinst Sample in Action

I did some work on this sample, mostly rearranging for exposition, but the bulk of the work (and the credit!) goes to Thomas Delrue, Alejandro Trigo, and Giovanni Della-Libera for their work on modeling WiX, translating "M" into WiX, and producing an MSI from SQL data. I could never have written this article in the time I did without their ground-breaking work. Also, thanks to Josh Williams, Martin Gudgin, and John Doty.

Chris Sells is a Program Manager for the Connected Systems Division at Microsoft where he is working on next-gen application development techniques. More info about Chris and his various projects can be found at