Adding a New Attribute to hcOPF

In order to support a client using SQL Server with replication I needed to add GUID support to hcOPF.  This post is a chronicle of my efforts.

If you’re unfamiliar with ThcAttribute you can breathe a sigh of relief.  It’s analagous to a barebones TField implementation which contains two native Delphi scalar fields used to store the original value of the attribute as read from the object store, and the current value as manipulated by the user.  It also contains two booleans to track whether the value of either field is actually NULL.  Using native Delphi fields of the corresponding type to the database field type minimizes the amount of memory required (a variant is 16 bytes), and still provides functionality similar to the Nullable types found in .NET.  If you’re talking to a database,  NULL <> ” or 0 so you need to provide full support for database NULLs.

First I created a new unit, hcGUIDAttribute and created a corresponding class descending from ThcAttribute.  Since a GUID doesn’t have many valid forms (you can’t convert it to a boolean, integer, or float for example) the number of methods that needed to be overridden is quite small.  Perhaps this is not the best example for what needs to be done when implementing a new attribute type, but it may provide some insight into the framework since ThcAttribute is a fundamental building block.

Since a GUID can really only be represented by a string, variant, or a TGUID we only have to override SetAsString, SetVarValue and their symmetric equivalents GetAsString, and GetAsVariant.

There are three abstract virtual methods on ThcAttribute.  All of these methods are declared as abstract because they must access the native Delphi fields used for the storage of values.  Since only descendants of ThcAttribute declare the private storage fields, these methods must be implemented in descendants, and the code is always the same:

SetOrigAsVariant():  This method sets the original value of the attribute as it was loaded from the database (assuming it ever was).  This method is used by the OriginalValue property to populate the native field FOriginalValue.  Since the framework populates the objects from the database, you may wonder why we need an OriginalValue property in the first place.  The answer is, so we can load single objects we know exist, from the datastore by populating it’s values and calling ThcObject.Read().  The framework also needs to be able to set an object’s OriginalValue as it propagates Primary Keys to child objects without knowing the native datatypes involved.

There are also two virtual methods that must be overriden in all descendants, but are not declared as abstract; ResetModified and UndoChanges.  These methods reset change tracking after the database has been updated, or reset the values back to those read from the database respectively,

Then I added code to ThcAbstractFactory.GetParameterValue for GUIDs and ThcAbstractFactory.PopulateAttributes method to populate the attribute from a database TField.  With a few miscellaneous support methods in the hcGUIDUtils unit  to generate new GUIDs and strip/add brackets to GUID strings it was ready.

Unlike most ORM/OPFs hcOPF supports complex PK/FK constaints and of types other than INT.  The SQL Server system I wrote that required GUID support has been running now for 10 years with minimal issues.

Leave a Reply