Persistate

Object Class Definition

Hide Navigation Pane

Object Class Definition

Previous topic Next topic No directory for this topic  

Object Class Definition

Previous topic Next topic Topic directory requires JavaScript JavaScript is required for the print function Mail us feedback on this topic!  

Object classes are at the core of the definition of the Model part of Persistate's Model / User Interface / Controller architecture.  The set of object classes in your definition file define the persistent object tree structure that your package works on.  Object classes are analogous to .Net classes, in that they encapsulate a set of members, which can be primitive data types, objects, collections or operations, the latter being entities analogous to methods.

The similarity is not quite one to one however.  For each object class that you define, Persistate does indeed generate a .Net class, and it does have generated properties for the data, objects and collections that you define.  However, the operations do not become methods in those generated classes.  Rather, you defines them in groups within the object class, and they become methods within a class generated for each operation group.  It is done this way in order to allow the operations to follow the Command design pattern.

Persistate also generates the necessary infrastructure to store instances of your classes in persistent storage.  This (for now) means database tables to hold the data, and the views, indexes and stored procedures necessary to access them.  A fundamental aspect of Persistate is that you never see or have to deal with this infrastructure.  When developing a Persistate application, you deal only with the .Net classes generated from your definition file.

Here are a couple of examples of object class definitions.

Each publication in the library is a stable persistent containing
   a nominative title,
   a distinctive ISBN number,
   several ordered stock copies
.

Each stock copy of a publication is an orderable persistent containing
   a discriminative name = "ParentPublication.Title + "" ("" + Order + "")""",
   a lend status,
   an associated lend from the library lends,
   operation group stock copy containing {
      the operation withdraw copy with context visibility
   }
.

The publication class has three members.  Two of these are primitive data - title and ISBN number.  It also has a collection of stock copy objects.  The stock copy class has four members.  Two of these are primitive data - name and lend status, the former being a calculated member.  It has an operation group member, also called stock copy.  These are described in the Operation and Operation Group Definition section.

The stock copy class also has a lend object member.  This is marked as associated, meaning that the stock copy does not contain or own the lend.  On the other hand, a publication object does contain its stock copies - the collection is not marked as associated.  This distinction is fundamental in Persistate.  The persistent tree described in your definition file is created by the containment relationships between the defined object classes.  This is often referred to as the containment tree, as opposed to the class hierarchy tree.

Of course, all trees must have a root.  In Persistate, these are called singletons, as they are implemented by classes which have a single instance only.  You define a singleton starting with The instead of Each.  You are allowed to have any number of singletons in your definition file, so your persistent object space can contain several trees.  Persistate generates a class for you called The which has properties for each singleton, so for example you would access the singleton library object using the code The.Library.

On a wider scale, however, all singletons are contained in Persistate infrastructure objects called EntryPoints, which are themselves contained in Environments. These are in turn contained in Domains, which are contained in the Internet.  The Internet object is the only true singleton in a Persistate system, and all persistent objects in all Persistate installations are conceptually all part of a single global persistent object tree under this root object.

Associated members of an object class involve references from one part of the persistent tree to another.  So the stock copy object has a lend member, but does not contain the lend object.  The lend is contained directly by the library singleton, and in fact the stock copy defines where in the persistent tree its lend member comes from, using  a from clause.  Every object in it has a single parent object - the object that contains it - but can have any number of associators.

Syntax

ObjectClass = ( "each" | "the" ) ObjectClassName [ Parent ] "is" ( "a" | "an" ) 
              CategoryAndBase "containing" Member { "," Member } ;
ObjectClassName = <symbol> { <symbol> } ;
Parent = ( "of" | "in" ) ( "a" | "an" | "the" ) ParentName ;
ParentName = <symbol> { <symbol> } ;
CategoryAndBase = <symbol> { <symbol> } ;
Member = ( Property | OperationGroup ) ;
Property = Quantity PropertyQualifiers PropertyName 
           [ ( "=" <string> | ( "from" | "within" ) FromOrWithinClause ) ] ;
Quantity = ( "a" | "an" | "one" | "several" | "many" | "selected" ) ;
PropertyQualifiers = { ( "associated" | "diverse" | "readonly" | "distributed"
                       | "discriminative" | "nominative" | "distinctive" 
                       | "volatile" | "ordered" | "optional" ) } ;
PropertyName = <symbol> { <symbol> } ;
FromOrWithinClause = <symbol> { <symbol> } ;

See Grammar Notation in Syntax Sections for an explanation of the format and symbols used here

Description

ObjectClass

This defines an object class.  Use The to start the definition if you want to define a singleton class, or Each for normal classes.  You can optionally define a Parent clause if you know that objects of this class will only ever be contained within objects of one other class.  If you do this, then try and define a contained member of this type in any class other than its defined parent, this will cause an error.

ObjectClassName

The name for your class can be one or more symbols.  Persistate will convert these to Pascal case to create the name of the generated .Net class.  So the stock copy class will become a generated .Net class called StockCopy.  This means that you must name your classes so that this conversion produces a valid .Net class name.  Also, the name must be unique within the set of all data classes, object classes and view classes in your package.

Parent and ParentName

If you include this optional clause, then you can only have contained members of this class in the class you name here.  The parent class must be in either the package you are defining, or in a package you imported using a package reference.

CategoryAndBase

This must be included and contains both the class category name and the name of the base class - the object class that this class derives from.  For the category name, you can choose any class category defined in any package in scope.  See Class Category Definition for details.  For the base class, Persistate supports a full class hierarchy, which is reflected in the .Net classes that Persistate generates from you definitions.  If you don't want to derive from any other class, then put Persistent here. Persistent is the base class for all persistent objects in Persistate.

Member

A Member of an object class can be either a Property or an OperationGroup.  The latter are described in Operation and Operation Group Definition.  The remainder of this topic deals with the definition of properties.

Property

When a definition file is processed, Persistate generates .Net properties in the generated class for the properties you define in an object class.  The mandatory elements in a property definition are the quantity and property name.  The latter also defines the class of the property - see the section below for details.  Optionally, you can attach property qualifiers, and a from clause or a calculation string.  All except the calculation are described below.

You create a calculated property by appending an equals sign and an expression in double quotes.  The expression can be any valid expression in the target language of your package.  (For the moment only C# is supported.)  This expression will be used verbatim in the generated .Net property.  The advantage of having arbitrary expressions as Persistate properties is that they can participate in Persistate user interfaces in the same way as normal properties.

Quantity

This determines whether the property is a single item, or scalar, or is a collection of items.  There are three types of collection in Persistate, and they are handled differently by the infrastructure, as detailed below.

Use a, an or one interchangeably to denote a scalar member.
Use several to denote a small collection.  The implication of using several is that all existing elements in such a collection are always in the collection in memory.  Whenever you access the collection in code, Persistate always retrieves the entire collection from persistent storage.
Use many to denote a large collection.  In this case, you must retrieve the specific subsets of elements that you want to use.  Persistate generates a number of methods which help you do this, based on which members of the class of the collection elements are marked as distinctive, discriminative or nominative.
Use selected to denote a collection of view class elements.  See View Class Definition to details of defining view classes.  Similar to many collections, you must retrieve the elements you want.  Persistate generates a method for you to allow you to perform this query.

PropertyQualifiers

You can add the qualifiers you need to a property in any order.  Where there are rules determining the compatibility of qualifiers to each other, the quantity or other aspects of the property, these are given in the following table.

associated

This qualifier means that this class does not contain the member object or collection of objects.  If the member is a scalar object, then it can be contained by any other object anywhere in the persistent tree.  If it is a collection, the elements can come from any number of different objects.  You can suggest to Persistate where the associated object(s) should come from by including a from or within clause - see below.  A typical use for an associated property would be to refer to an item in a lookup table.

You can use the associated qualifier only with object members, not with members having a data class.  You cannot combine it with the optional modifier.

diverse

Use diverse to indicate that a collection may contain objects of the specified class, or of any subclass of the specified class.  Without this qualifier, collections can contain only objects of the exact specified class.

This qualifier can be used only with collections,

readonly

Using this qualifier means that the .Net property generated will have a getter but not a setter.  However you will still be able to alter the underlying (private) field value, and the value will still be persisted.

This qualifier cannot be combined with the volatile qualifier.

distributed

Use distributed to indicate that a collection can be distributed over more than one database.  Without this qualifier, all objects in a collection must be held in the same database.  Don't specify this qualifier unless you need it, as it obviously entails extra work by the Persistate runtime.

This qualifier can be used only with collections, and cannot be combined with the volatile qualifier.

discriminative

If you mark a property with the discriminative qualifier, you are implying that this property can be used to discriminate one object of this class from another.

Persistate uses this fact and includes the property in automatically generated lookup forms used to access elements of many type collections, as well as generating a method to query a many collection based on the value of the property.  In persistent storage, Persistate will create an index on the values of discriminative properties.

This qualifier cannot be used with collection properties.  Note that if you specify the nominative or distinctive qualifiers, the property is also automatically set as discriminative.

nominative

Use the nominative qualifier to indicate that a property is part of the "name" of an object.  Persistate uses all properties marked as nominative to generate a .Net property called NominativeText.

This is used wherever Persistate needs to refer to an object in the user interface, for example when asking for confirmation to delete and object. Nominative can be seen as a special type of discriminative, so everything that applies to the latter applies to nominative also.

This qualifier cannot be used with collection properties.

distinctive

The distinctive qualifier is also related to discriminative, so that everything that applies to the latter applies to distinctive also.  In addition to that, the distinctive qualifier implies that in any collection of objects of this class, the values of distinctive properties must be unique.

Collections in memory of this object class have an indexer for each distinctive property which allows you lookup the unique element with a given value.  For many type collections, the method for querying the collection on the value of a distinctive property returns a single object value.

This qualifier cannot be used with collection properties, and can be used only with properties with a data class, not an object or view class.

volatile

The volatile qualifier indicates that the property will not be held in persistent storage.  Use this if you want a property that can participate in a Persistate user interface, but which has no requirement to be persisted.

If you want the entire object to be volatile, then you can use the volatile class category when defining the object class - see Class Category Definition for details.

You cannot combine volatile with the readonly or distributed qualifiers, not can it be combined with many or selected collections.

ordered

Use the ordered qualifier with collections to ensure that particular objects are always at the same index in the collection. Ordered collections behave slightly differently to non ordered ones.  See the API documentation for the Persistate.IndexedList class for details.

The ordered qualifier can be used only with several type collections.  The property must have an object class, and this class must have a class category which has the allow order behaviour - see Class Category Definition for details.

optional

Include this qualifier to inhibit the automatic creation of contained scalar objects for the property when an object of this class is created or initialised.  Persistate generates a method for each object class called InitialiseMembers, in which all scalar contained object properties are initialised to a new object created with the zero parameter constructor.  Use optional to inhibit this.

This qualifier can only be used with scalar properties, not with collections, only with properties with an object class, and cannot be combined with the associated qualifier.

PropertyName

The property name you give here is shown in the syntax above as one or more symbols.  What this does not show is that these symbols define both the name and the persistent class of the property.  The class name that you supply as part of this can be a data class name, object class name or view class name.  The class must be in either the package you are defining, or in a package you imported using a package reference.

If you are defining a collection property, you must use the plural form of the class name.  Persistate holds a list of all the common irregular plurals in English, and for the rest, forms plurals in the standard way..

Future Enhancement

For now, the irregular plural list is fixed.  In or before version 1.0, you will be able to add irregular plurals from the Persistate Generator add-in.

You can specify the name of the property in one of three ways.  In all cases, the generated property name is constructed by converting the symbols used to Pascal case.

Specify just the class name.  In this case the name of the property is the same as the name of the class.  In the case where you have a single property of a given class, and the name fits, this avoids "name pollution".  In the following example, the generated property name will be the same as the class - IsbnNumber

a distinctive ISBN number,

Specify a property name separately from and before the class name.  In this case, the separate property name alone is used for the generated property name.  In the following example, the generated property name will not include the class name (datetime) - it will be Borrowed.

a borrowed datetime, 

Specify symbols that will be combined with the class name to create the property name.  to do this, append an underscore to the last symbol before the class name.  In the following example, the generated property name will include both the additional symbol and the class name  - it will be LentStockCopies.

several associated lent_ stock copies,

In all cases, the name that you specify must be unique within the set of members of the object class, including any operation group members.

FromOrWithinClause

You can add a from clause or a within clause to any associated property, whether scalar or collection.  The clause informs the Persistate runtime where the object(s) that will be associated to the property are located in the persistent tree.  Persistate uses this information to give users the appropriate choice of objects when setting the property value in a user interface.

Future Enhancement

If the associated property is set programmatically, Persistate does not check that associated objects do actually come from the location specified.  In a future version, this may change.

The difference between from and within clauses is that a from clause specifies a collection, and the object(s) to be associated come from the elements of that collection.  A within clause specifies an object, and the object(s) to be associated come from any location within (under) that object in the persistent tree.  Syntactically, these clauses are just one or more symbols, so how do you write them?  They are traverses of the persistent tree, starting from either the object containing the property or a package singleton.

To start from the the property's parent object, use its to start the traverse.  Subsequent words should donate either the name of an object property (contained or associated) or be the word parent.  The traverse then happens in a similar way that you would traverse a .Net object graph using the standard dot notation, except that  here property names simply follow each other separated by white space.  Here is an example from the Library package in An Example Definition File.

Each lend in the library is a transitory persistent containing
   a discriminative associated publication from the library publications,
   an associated stock copy from its publication stock copies,

To start from a package singleton, use the to start the traverse.  The next word(s) should be the name of one of the singleton classes in the package, putting the traverse in that object.  Similarly to above, subsequent words should donate property names, or be the word parent.  Note that you can use parent only if the class concerned has a Parent clause in its definition (see above).  Here is an example.

Each author in the library is a stable person containing
   a publisher text,
   several associated books from the library publications,

From clause traverses can go through any number of scalar objects, but must always end at a collection.  Furthermore, the collection must contain objects of the appropriate class to be assigned to the associated property. Within clause traverses have no such constraint - they can end on any object or collection of any type.  Here is an example from the Device Network package from the Creating and running the Device Network demonstration walkthrough.

Each cable in the network is a stable persistent containing
   a distinctive nominative name,
   a discriminative associated cable type from the network cable types,
   a discriminative cable_ colour,
   several associated plugged into connectors within the network,