Inheritance

Modified on 2010/01/05 00:32 by olmo — Categorized as: SignumEntities

Introduction

Inheritances (and interface implementation) is maybe the most popular feature of Object Oriented Programming since encapsulation is for paranoid people and nobody understands polymorphism :) and it is the one that has more difficulties to map to the relational world.

Signum Framework approach is a very practical one. We put all the responsibility of inheritance mapping on Polymorphic Foreign Keys because they allow three different scenarios, all of them very important:

  1. Map a hierarchy of classes of your data model in the database: This problem is the one everybody thinks of when speaking about ORM and inheritances mapping. Nothing new here, but actually it's not that common in real data models. Maybe it is more interesting for the intellectual satisfaction than for practical reasons.
  2. Reference to potentially 'every entity': Some entities like notes, attached documents, alerts... are potentially attachable to any kind of entity. It's like having System.Object in the database.
  3. The combination of polymorphic foreign keys with overriding Attributes through the SchemaBuilderSettings opens a new field in application logic reusing. For the first time, you can write a vertical module of functionality (from the UI to the DB) in a different assembly and integrate it easily. Not joking. Polymorphic foreign keys and the discipline we impose over entities let you glue database tables as easily as entities. Read more in Signum Philosophy and Vision.

At the end of this page you could read a bit more about why we ended up implementing inheritance in this way.

So Inheritance is all about Polymorphic Foreign Keys (PFK). What the hell are they???

A PFK is just FK that could point to entities of different types. We have them in two flavours:

ImplementedByAttribute

This FieldAttributes take params Type[] as constructor, what it does is to allow the field to have objects of any of the defined types. I.E:

[ImplemetedBy(typeof(GunDN), typeof(BazookaDN), typeof(MachineGunDN)]
IGun gun;

The actual implementation in the database is just multiple foreign keys, each one to different tables of each mapped type (GunDN, BazookaDN, MachineGunDN). Due to that, the types should be:


Finally, if you find yourself having many fields of the same type (IField) in different classes (field mapping), and all of them mapping the same concrete types (Gun, Bazooka and MachineGun), then you can move the ImplementedBy attribute from the field to the top of the common field type (IGun), and remove the duplication (type mapping).

Once in this situation, if a field is still mapping to different types, it will have priority over type mapping.

ImplementedByAllAttribute

This FieldAttributes, instead of mapping a finite number of Types and creating this number of FK in the database, assumea that almost 'every entity' could fit in this field.

[ImplemetedByAll] // there are too many kinds of weapon in the world to enumerate it...
IdentifiableEntity weapon;

The implementation in the database uses just two columns:


Type ID? Does .Net entities Type have some kind of ID?.

Unfortunately not, and since the type name could change with refactorings we have to keep a table of TypeDN, and create a FK from the second column of an ImplementedByAll field to this table.

Think of TypeDN as Signum Engine's equivalent to System.Type.

That's all you need to know about Inheritance in Signum Engine.... unless you want to know more :)

Polymorphic Foreign Keys Uses (Advanced Topic)

Lite<T> support

You can use Lite behaviour through the Lite<T> seamlessly. In fact, the whole reason Lite<T> needs RuntimeType internally is to support these kind of scenarios.

[ImplemetedByAll] // there are too many weapons in the world to enumerate it...
Lite<IdentifiableEntity> weapon;

Setting the Schema Up

As we have said, the ability of using SchemaBuilderSetting to override Attributes on entities that you don't own, lets you integrate these entities with your current application in a type-safe and elegant way. Take a look in Schema.

Save and Retrieving

Nothing to learn. Saving and retrieving entities with PFK it is just transparent.

Query

We are very far from that, but just in advance, we support polymorphic foreign keys in queries also:


In the new version it's also possible to access members of a ImplementedBy class or interface (MList not supported jet). This is great for application composability on queries. See more in queries.

Why we do not support other inheritance solutions? (Advanced Topic)

Given the next simple hierarchy:

    public abstract class PersonDN : Entity
    {
        string name;
        (..)
    }

public class SoldierDN : PersonDN { WeaponDN weapon; (..) }

public class TeacherDN : PersonDN { BookDN book; (..) }


There are different ways of persisting with inheritance hierarchies, using NHibernate's terminology:

1. Table-per-class-hierarchy: A table PersonDN {Id, ToStr, Discriminator, Name, idWeapon, idBook, }. Every Soldier and Teacher goes to the this table using Discriminator values {'S', 'T'} to differentiate between them. This approach has some problems:
2. Table-per sub-class: Here we have three tables, PersonDN(Id, ToStr, Name), SoldierDN(idPerson, idWeapon) and TeacherDN(idPerson, idBook). Problems:
3. Table-per-concrete-class: In this solution, you will have just two tables, SoldierDN(id, ToStr, Name, idWeapon) and TeacherDN(id, ToStr, Name, idBook). Since PersonDN is an abstract class there's no point in having its own table. Problems:
When we designed inheritances in our framework we went just for the third way because it is the simplest.
However, the main reason for going for the PFK-only solution is that all these complex solutions solve only the initial problem num. 1: Saving Entity Hierarchies, while PFK also allows the second and third scenario, that are almost as useful as the first one.