The documentation comes from the Markdown files in the source code, so is always up-to-date but available only in English. Enjoy!
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:
SchemaBuilderSettings allow us to connect different modules (from the UI to the DB) in a different assembly and integrate them easily.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 flavors:
This FieldAttributes take a params Type[] as constructor, what it does is to allow the field to have objects of any of the defined types. Let's supose we have a PlayerEntity entity with the following field:
[ImplemetedBy(typeof(RevolverEntity), typeof(BazookaEntity), typeof(MachineGunEntity)]
IWeapon weapon;The actual implementation in the database is just multiple foreign keys, each one with different tables of each mapped type (RevolverEntity, BazookaEntity, MachineGunEntity). Due to that, the types should be:
Entity (they need their own table)IWeapon).When there are many common fields in the different implementations of an ImplementedBy field (in this example, if RevolverEntity, BazookaEntity and MachineGunEntity share many fields, declared in IWeapon) writing polymorphic foreign key can be quite slow. For example:
Database.Query<PlayerEntity>().Select(p=>p.Weapon.Ammunition > 0)This query will need to join with the three different implementations and coalesce each of the three Ammunition columns. This case is even worst:
Database.Query<PlayerEntity>().Select(p=>p.Weapon.Provider.Name)In this case, the ProviderEntity table will be joined with the three different tables, creating a slow join due to the use or ORs.
In our experience, the best idea many times is to fusion all the common fields in a single class (WeaponEntity) with an field of type WeaponExtensionEntity implemented by RevolverWX, BazookaWS and MachineGunWS containing the different fields.
When this is not an option, use CombineStrategyAttribute on the ImplementedByField, or use CombineSwitch and CombineUnion extensions method on each particular query could help you tuning the performance using SQL SWITCH (default) or UNION in each case.
This FieldAttributes, instead of mapping a finite number of Types and creating this number of FK in the database, assumes that almost 'every entity' could fit in this field.
[ImplemetedByAll] // there are too many kinds of weapon in the world to enumerate it...
Entity weapon;The implementation in the database uses just two columns:
TypeEntity table.Think of TypeEntity as Signum Engine's equivalent to System.Type. It's a table containing a row for each concrete Entity included in the schema.
That's all you need to know about Inheritance in Signum Engine.... unless you want to know more :).
You can use PFK with Lite seamlessly. In fact, the whole reason Lite are covariant is to support these kind of scenarios.
[ImplemetedBy(typeof(RevolverEntity), typeof(BazookaEntity), typeof(MachineGunEntity)]
Lite<IWeapon> weapon;The ability of using SchemaBuilderSetting to override attributes on entities that you don't control lets you integrate different modules in a type-safe and elegant way. Take a look in Schema.
Nothing to learn. Saving and retrieving entities with PFK it is just transparent.
We support polymorphic foreign keys in queries also.
ImplementedBy references and ImplementedByAll references in any combination!!Given the next simple hierarchy:
public abstract class PersonEntity : Entity
{
string name;
(..)
}
public class SoldierEntity : PersonEntity
{
WeaponEntity weapon;
(..)
}
public class TeacherEntity : PersonEntity
{
BookEntity book;
(..)
}There are different ways of persisting with inheritance hierarchies, using NHibernate's terminology:
PersonEntity {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:TeacherEntity.PersonEntity { Id, ToStr, Name }, SoldierEntity{idPerson, idWeapon} and TeacherEntity{idPerson, idBook}. Problems:Id column in SoldierEntity (or TeacherEntity),then ArmyEntity and SchoolEntity will have the same problems for referencing concrete classes.Id column on SoldierEntity (or TeacherEntity), you will have two different Ids: For a soldier idPerson and idSoldier, and for a teacher idPerson and idTeacher. This creates ambiguities.SoldierEntity { id, ToStr, Name, idWeapon } and TeacherEntity { id, ToStr, Name, idBook }. Since PersonEntity 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 option #3 because it is the simplest.
With #1 and #2 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 not suported by #1 or #2.
The algorithm to know where an entity actually resides becomes more complex with hierarchies and we also 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 first initial (Saving Entity Hierarchies), while PFK also allows solve the 2nd problem (reference to anything) and 3rd problem (connect modules), that are almost as useful as the first one.
© Signum Software. All Rights Reserved.
Powered by Signum Framework