If you have worked with ObjectSpaces you might wonder what hidden members are all about. The documentation is pretty slim here:
“If you do not want to display the unique identifier value for a particular class, you can use the Hidden attribute to specify whether the Member is visible by the class or not.”
Here is a sample of how you might use the hidden attribute on a member declaration in your .osd file:
If you try to hide a member that exists in the corresponding class, you will get a ObjectSchemaException. So how would you use it, then?
Well, here’s the lowdown on hidden members. The hidden members exist to create fields that will be mapped, but do not exist in your class. You could call them virtual fields, because these fields do exist for ObjectSpaces, are persisted in the data source, but have no corresponding member in the persistent class. To put it another way, the hidden members are retrieved from the data source, are tracked inside the ObjectContext, but just don’t make it inside the objects as a actual member. Hidden members are inaccessible from your objects.
So far, I figured that there are two possible uses for hidden members:
- Create a key member for a class that does not have one itself.
- Use extra members that are used for concurrency checks when updating or deleting objects from the data source.
Let me elaborate a little.
On point 1:
When your class that needs to be persisted does not have a key member itself, you have the choice to add such a member to your class, or not. For the second choice you can specify a key member and mark it as hidden. This can be very handy in cases where you have no access over the source code of the class. Again, the hidden member is used to mark the key field as non-existent in the persistent class. However, since it does exist in the ObjectContext and is mapped, the member/field makes it way into the data store when the objects are persisted. This way you can still add a key to your objects from a database perspective, without making changes to the class.
The key member will need to have a key type. Any key type is possible, except for the User type. This leaves you with AutoIncrement, Guid and Custom. The reason that you can’t use User is simple: you have no way to programmatically set the value of the member. It only exists in the ObjectContext, not in the object. The engine of ObjectSpaces will take care of assigning it for you, based upon the type of key. It will generate an autoincrement integer, a new Guid or use the KeyGenerator class if you specified a custom key type.
You probably got it by now. Let me just add the context of it. Let’s assume you map the Person class:
that has an osd declaration like above. The .msd file will map all three members (one of which is not represented in the class) to the data store.
The database table Persons will have three columns, for Name, Birthday and Id.
You can check for yourself that the ObjectContext does contain the hidden member, where (obviously) the Person class does not have such a member:
This will print the newly assigned key member value from the ObjectContext.
As a side note: the Object Persistence Toolkit (OPT) also makes use of this strategy to create mapping files and database tables for every class that you ask it to persist. It will add a hidden key member to the class, as it is unable to determine for a particular class (say the Person class from above) if there is a member that could serve as the key member. It will add a new one to avoid wrong assumptions based upon the names of member. The OPT restricts the key type to an autoincrement member. I’m not sure why that would be. I’ve successfully tested with a Guid hidden key member.
On point 2:
Imagine that you created a class that has members for a subset of the columns of a certain table. When you try to update or delete your objects to the data source, you might want to check for concurrency errors. Normally you can only do this based upon the members that are actually retrieved, because they exist in the persistent classes and are mapped. With hidden members you can retrieve not just the columns for the members of your objects, but also additional columns, that are tracked in the ObjectContext. These are by default used for concurrency checks. This way, you can tell when the original record has changed, based upon the checks on database columns/fields that you would be unable to alter yourself.
BTW, it would be a weird combination to mark a non-key member as both Hidden=”true” in the osd file and UseForConcurrency=”False” in the msd file.
There it is: hidden members. If you have any comments or can think of other uses for hidden members, feel free to leave your thoughts.