How Relationships are Modeled in "MSchema"

Oslo

I've been playing a lot with M to do my database and test data loads for some internal projects for AgiliTrain and I've noticed an interesting representation for how relationships in MSchema are generating their SQL. In MSchema you have a couple of ways to represent a relationship between tables. The relationship syntax is one feature I really like in MSchema.  For example, let's take the Northwind database's Customers and Orders tables. In MSchema I can define them in a traditional way where the Order has a reference to the Customer table:

module Northwind
{
  type Customer
  {
    CustomerID : Text#5;
    CompanyName : Text#40;
    ContactName : (Text#30)?;
    ContactTitle : (Text#30)?;
    Address : (Text#60)?;
    City : (Text#15)?;
    Region : (Text#15)?;
    PostalCode : (Text#10)?;
    Country : (Text#15)?;
    Phone : (Text#24)?;
    Fax : (Text#24)?;
  } where identity CustomerID;
  
  type Order 
  {
    OrderID : Integer32 = AutoNumber();
    OrderDate : (DateTime)?;
    RequiredDate : (DateTime)?;
    ShippedDate : (DateTime)?;
    Freight : (Decimal9)?;
    ShipName : (Text#40)?;
    ShipAddress : (Text#60)?;
    ShipCity : (Text#15)?;
    ShipRegion : (Text#15)?;
    ShipPostalCode : (Text#10)?;
    ShipCountry : (Text#15)?;
    CustomerID : Customer? where value in Customers;
  } where identity OrderID;
  
  Customers : Customer*;
  Orders : Order*;
}

If you notice the CustomerID in the Order type, you can see that we are defining relationship in the type so that the database construction is very predictable. For example, an ERD of the database created looks like so:

Simple Relationships

My problem with this strategy is that when you define MGraph representations of the data, you have to define your objects separately. MGraph lets you assign names to do this so its pretty easy:

  Customers
  {
    BobsCrabShack {
      CustomerID = "BOBSH",
      CompanyName = "Bob's Crab Shack",
    }
  };
  
  Orders 
  {
    {
      OrderDate = 2009-01-01,
      Freight = 199.00,
      CustomerID = Customers.BobsCrabShack
    }
  }

Notice that the MGraph uses the alias set up in the customer to assign it to the Order. This way that MGraph (or testing data in my case) does not need to ever know about generated ID's.  The SQL generation in the M Executor (mx.exe) takes care of that for us. There is a fundamental problem with this though.

For several situations (MGrammar's AST to MGraph for example), i'd like to nest the objects to imply that owner ship.  For example, I'd like to craft my MGraph like this:

  Customers
  {
    BobsCrabShack {
      CustomerID = "BOBSH",
      CompanyName = "Bob's Crab Shack",
      Orders = 
      {
        {
          OrderDate = 2009-01-01,
          Freight = 199.00
        }
      }
    }
  };

You should notice that the orders for Bob's Crab Shack are nested inside the Customer.  When you're dealing with a tree of information, constructing the MGraph would be easier in this way. But this requires we look at the relationship differently in MSchema:

module Northwind
{
  type Customer
  {
    CustomerID : Text#5;
    CompanyName : Text#40;
    ContactName : (Text#30)?;
    ContactTitle : (Text#30)?;
    Address : (Text#60)?;
    City : (Text#15)?;
    Region : (Text#15)?;
    PostalCode : (Text#10)?;
    Country : (Text#15)?;
    Phone : (Text#24)?;
    Fax : (Text#24)?;
    Orders : Order*;
  } where identity CustomerID;
  
  type Order 
  {
    OrderID : Integer32 = AutoNumber();
    OrderDate : (DateTime)?;
    RequiredDate : (DateTime)?;
    ShippedDate : (DateTime)?;
    Freight : (Decimal9)?;
    ShipName : (Text#40)?;
    ShipAddress : (Text#60)?;
    ShipCity : (Text#15)?;
    ShipRegion : (Text#15)?;
    ShipPostalCode : (Text#10)?;
    ShipCountry : (Text#15)?;
  } where identity OrderID;
  
  Customers : Customer* where item.Orders <= Orders;
  Orders : Order*;
}

 Two things to notice in the MSchema example, the relationship is defined in the parent not the child. The Customer type now has a property called Orders that is of type "Order*". So that the Customer is expected to have a list of orders. In the Customers extent, we create the constraint to say that each Order in the Orders property is in the Orders extent. (A bit confused by the identical naming but I think you can make sense of it).

So that is all good, right?  Well, it is almost fine. The schema generation of this pattern is decidedly different:

Ownership Relationships

Note that the relationship is now represented by a mapping table between the Orders and Customers. At first I thought this was just a mistake, but in fact its the right thing to do.  Why?  Because the MSchema language has no way of us telling it that the Orders are a type that are naturally 'owned' but Customers.  In other words, Orders will have one and only one parent: Customers. Since MSchema can't describe that, it does the right thing and use a mapping table to establish the relationship so that other objects that contain Orders can establish their own relationship.

I'd love to have a way to have my cake and eat it too. If there was a way to specify that we were the one and only owner of a particular relationship, then it could create the SQL in a smarter fashion, but that feels like its trying to be too smart and might cause confusion.

If you want to play with the two styles, I've included a .zip file (linked above) with the two "M" files.

What do you think?

Comments:

Gravatar

Shawn,

Oslo seems a bit like Lost at this point. If you haven't been following the plot closely, is it possible to catch up at this point?

For instance, what I'm seeing here is that MSchema/Oslo will let us build a DSL that will let us generate T-SQL to create tables in our database. Which is cool, but at the same time sounds like an evil plot Ben Linus came up with.

I know that's not the case but ... can you post about what Oslo is rather than the typical posts I've been reading on other sites either: explaining what it is _not_ or providing technical implementations for how to do something with Oslo without explaining why we would do it with Oslo?

Oslo is clearly the ubergeek technology at this point, and I'm attracted to it like a moth to a flame -- but after several months of reading, I feel like I understand less and less what it is.

Yours sincerely,
Dazed and Confused about Oslo

Gravatar

That's a great idea. Let me write something up tonight.

Gravatar

I was just going to come in and say 'what is all that for...' then I saw the above post and laughed, because I feel the same way.

I keep thinking...we have a 'DSL' to do this - it's called 'ORM' - setup NHibernate mapping files and say 'generate database' :)

Gravatar

There are too many concepts on ORM, I prefer Microsoft's Entity Framework.

Gravatar

Jack,

MSchema is *not* ORM...it a way to design and store metadata, not an ORM. In fact, once you use MSchema to design the database (read my "Why Oslo Matter to Me" entry for a better explanation), you would use data access methodologies (like EF, another ORM or even straight data acceess) to get at the data.


 



 
Save Cancel