Friday, January 04, 2008

Last post showed a very simple database setup with a Person, a Customer and an Employee-table. They were mapped to different classes in what is called a Table per Type mapping.

A different kind of mapping strategy is the Table Per Hierarchy mapping, where all the types in your hierarchy are mapped to the same table. In many cases your dba will not understand why there should be different tables for information that can clearly be represented within the same table! For instance, different types of employees (developer, manager, architects) will certainly re-use the employee table, where very often a column is added to differentiate between these types.

Far be it from me to disagree with such a database-schema. It makes perfect sense and is a clear example of the Object relational impedance mismatch. The domain-model needs to break free of these kinds of reasoning and needs to define it's own hierarchy.
It can do so by leveraging the column that was added to diffentiate between types. In OR-Mapping, it is called a discriminator column, as the system can use it to discriminate between different the class-type it should use.

I added a number of fun classes to the model, that represent developers: Einstein, Elvis and Mort. In this company, each inherits properties from the other, in a possibly unexpected way ;-)
There is also the Business analist and a Tester.

All these types have properties that are mapped to the same table. However, as your dba has come to understand, the different types of employees do differ a little and in time this has led to the introduction of a few extra columns in the employee table: 'ProgrammingLanguage' and 'TestMethodology'. Too bad these have to be nullable, because they don't make sense in all cases!
Also, the functionname column of the previous post was changed to functiontype (int) and will act as the discriminator.

Our database now looks like this:

EF_TPH_DBDiagram..jpg

Nothing fancy here.

To create the conceptual model, I could use the designer. Again, if I update from the database, the model becomes useless, but editing the SSDL was easy enough in this case.
Adding the new entities in the model designer was also easy:

  • There should not be a scalar property 'FunctionType' in the model. The discriminator works behind the scenes, and that is a good thing! As a user of the domain model, you should not need to know about it.
  • Only when you delete that property, can it be used as a 'condition', which basically is the condition that EF uses to see which class-type it should instantiate.
  • To map the condition, you need to map the various types to the employee table, even though they are already mapped through their parent 'Employee'!! No properties need to be mapped though.
  • When adding the condition, a value can be set. When you set the value, the designer does make an error in the xml which you will have to fix yourself!! So, when I wanted the type 'Einstein' to have the condition 'FunctionType = 1', I let the designer create it, and went to the line the build error indicates as being wrong. There I changed some weird value like '_x3301' to just '1'.

The model now looks like this:

EF_TPH_ConceptualModel..jpg

Using the following test code:

....

Einstein smartNerd = new Einstein();
smartNerd.Firstname = "Albert";
smartNerd.Lastname = "Einstein";
smartNerd.Language = "C# 3.5";
context.AddToPerson(smartNerd);

Elvis elvis = new Elvis();
elvis.Firstname = "Elvis";
elvis.Lastname = "Presley";
elvis.Language = "C# 2.0";
context.AddToPerson(elvis);

Mort mort = new Mort();
mort.Firstname = "Mort";
mort.Lastname = "Builder";
mort.Language = "VB.Net";
context.AddToPerson(mort);

BusinessAnalist George = new BusinessAnalist();
George.Firstname = "George";
George.Lastname = "Clooney";
context.AddToPerson(George);

Tester tester = new Tester();
tester.Firstname = "UnitTests";
tester.Lastname = "Rock";
tester.Methodology = "TMap";
context.AddToPerson(tester);

....

I got the following expected outcome:

Ian, Mort is a Employee
Albert, Einstein is a Einstein
Ruurd, Boeke is a Person
Silvia, Banana is a Customer
George, Clooney is a BusinessAnalist
UnitTests, Rock is a Tester
Elvis, Presley is a Elvis
Mort, Builder is a Mort

The employee table was filled like this:

55 0 NULL NULL
56 1 C# 3.5 NULL
59 4 NULL NULL
60 5 NULL TMap
61 2 C# 2.0 NULL
62 3 VB.Net NULL

So, that's all there is to it.
Hopefully the data-team will fix the mapping bug in the next ctp!

Next up, I will mix it up just a bit.

Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):