Signum Framework Logo
"Open framework that encourages convention over configuration, using C# code,
not XML files, to model at the right level of abstraction and achieve deadlines.
...but also has a full Linq provider, and syncs the schema for you!"
Login
RSS

Search

»



Main
Index
Map
Videos
Download
Source code
Tutorials
Forum
FAQ



Image



PoweredBy

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:

  • A subclass of IdentifiableEntity (these need their own table)
  • A type that fits on the Field's Type (in this case, an implementation of IGun).

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:

  • One for the actual Id of the related entity. This column has no Foreign Key restriction.
  • Another for the Type ID of the Entity.

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:

  • Navigate across PFK to an actual implementation just by Casting the field in your queries.
  • Test the type of a PFK just by using is operator.
  • Test two entities for equality. Our entity comparison algorithm on queries takes PFK into account, you can equalize normal references, ImplementedBy references and ImplementedByAll references in any combination!!
  • Retrieve and ToLite(): You can retrieve and take Lites of elements in any PFK easly, there's just nothing to learn.

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:
  • It works only with hierarchies that are not very deep, or you will end up with a lot of null values.
  • Type mismatch appears, since Soldier could have, let's say, int NumberOfKilledPeople, that would need to be null in case the row contains a TeacherDN.
  • It's not easy for an army to have a list of Soldiers, or for a school to have a list of teachers, since they all are in the same table.

2. Table-per sub-class: Here we have three tables, PersonDN(Id, ToStr, Name), SoldierDN(idPerson, idWeapon) and TeacherDN(idPerson, idBook). Problems:
  • Relationally, you could have someone who is both Soldier and Teacher. This is valid in the real world but not in the Object model :)
  • If you don't put an Id column in SoldierDN (or TeacherDN),then ArmyDN and SchoolDN will have the same problems for referencing concrete classes.
  • If you put Id column on SoldierDN (or TeacherDN), you will have two different Ids: For a soldier idPerson and idSoldier, and for a teacher idPerson and idTeacher. This is not a good thing.

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:
  • Not easy to have a Foreign Key to Persons in a generic way.

When we designed inheritances in our framework we went just for the third way because it is the simplest.
  • With the first and second options you need to add a 'hierarchy concept' in the framework, something that embraces the three classes and puts them together in the same table (#1) or in the same table hierarchy (#2).
  • Since interfaces allow some kind of multiple inheritance, the same entity could potentially be part of different hierarchies, and this is a very bad situation.
  • The algorithm to know where an entity actually resides becomes more complex with hierarchies.
  • You avoid the type mismatch of solution #1.

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.
Creative Commons License Signum Framework Site by Signum Software is licensed under a Creative Commons Attribution 3.0 License.
Powered by ScrewTurn Wiki version 3.0.5.600.