Inheritance

Inheritance is a feature many people want but have a difficult time implementing. There are numerous ways of implementing this functionality. All of which have pros and cons. There are two basic ways to create an object database. The first is the put all fields in a single table from the entire object hierarchy. This may duplicate data across parent child tables but makes saving and loading simpler. The second way is to split the data logically based on the object hierarchy of your model. The properties of parent objects are stored in parent table fields and the child properties are stored in child table fields. This makes inserts, deletes, referential integrity, etc more difficult but in a generated framework there is no reason to care. The nHydrate generator opts for the latter. Different tables hold logical objects and are tied together with API.

Let us take a simple example of two entities: SystemUser and Customer. Let us say that Customer derives from SystemUser and both have a primary key of UserId. Now in code you would create a SystemUser class, then a Customer class that derives from it. In code, when you instantiate a Customer you actually have a SystemUser as well. You can of course cast the Customer as a SystemUser and use the properties and methods of the base class. This is completely possible to model and generate with nHydrate. You simply define your parent table in the model and define a relationship from SystemUser to Customer. In the generated code, there is a SystemUser class, as well as a Customer class.

The code works as expected but how is it saved? In the database, there is also a SystemUser and Customer table. There is also a one-to-one relationship from SystemUser to Customer. This relationship dictates that a Customer row cannot exist without a parent SystemUser row. This is just like your code. You cannot instantiate a derived class without also having its base class. You can cast to the base, so you essentially have a base class in memory as well. They are of course the same object but I am making a point. A Customer cannot exist without its SystemUser base. This relationship also defines the rule that all Customers are SystemUsers but not all SystemUsers are Customers.

Now in the database there must exist one row in Customer table for a Customer object, but also one row in the SystemUser table since the base properties are there. It is not possible to have a row in the Customer table without a corresponding row in the SystemUser table. This is all completely transparent to you the developer. In code, you only need create a Customer object and save. The back end API knows how to split the data across tables. When you load a Customer, the API knows how to load the data from different tables into your objects. In fact, you need not be aware of any of these details or even that the objects are derived in the first place. You can use them like any other object in the framework. That is the beauty of generated code. The entire complexity of inheritance hierarchies is hidden from you. You do not care how tables are implemented in the database or how each relates to the others. The database truly becomes a data repository that stores stuff for you. The details of the load and save are not anymore so important.

Let us expand this example to have multiple derived tables. Not only do we have a Customer derived from SystemUser, but we also have Employee as well. They are both SystemUser objects but play different roles in our system. Now when you create a Customer and save it, there is one row inserted into the Customer table of course and one row inserted into the SystemUser table. There are two database rows created for each Customer. Now when you create an Employee object, there are also two database rows created: one in Employee table and one in SystemUser. This may look odd to traditional relational database programmers. After adding one Customer and one Employee, the SystemUser table has two rows in it. This is completely logical when you think about what we are doing. We are splitting data across tables to facilitate object inheritance. This is not functionality that a relational database handles implicitly. We have turned our relational database into a sort of object database.

inherit.png

All of the relationships of the table and all base tables can be used as if the child object contained all of them. For example, if SystemUser has a relation to an EmailAddress table to hold multiple email address for an Entity, each Customer and Employee would also have this relationship. In fact, there is no way to know in code which layer has the relationship. It is only important that Customer and Employee have an EmailAddressList hanging off of them so the developer can walk the relationship and pull the needed data.
This functionality can be taken to any depth you desire. An object can be derived from another that is derived from another, and so on. A relationship is visible from all layers in any object above the level at which the relationship is defined. This also facilitates LINQ queries.

Inheritance is a powerful tool that has been a long time coming. The intricacies and problems associated with it are not easily solvable, which is why most applications avoid it all together. The nHydrate framework provides an easy to use interface to wrap Entity Framework to define and use inheritance hierarchies in code, with little understanding of the database particulars relating to persistence of such a framework.

Last edited May 8, 2012 at 12:15 AM by codetools, version 8

Comments

No comments yet.