Data objects are persistent objects which represents the domain. Data objects may have properties and indexed properties of primitive types, other data objects or reference codes.
Data objects have a unique identifier and a timestamp property which stores the time of the last update.
Data objects derive from the interface PmMda.Net.Dog.IDataObject
which is
described in the API.
Each data object can have a lightweight representation. The lightweight representation has just a subset of the data object properties and is read only. This representations are called lightweight objects. Lightweight objects are used if just a subset of the information stored in the data objects is used. Lightweight objects avoid that unneeded data is sent over the wire.
Typical usage of lightweight objects is displaying a navigation tree in the user interface.
The condition in which a lightweight representation is generated is described in the tagged values.
The lightweight objects derive form the interface PmMda.Net.Dog.ILightweightObject which is described in the API.
The associations of a data object are either properties or indexed properties.
Properties are 1..1 or 1..0-1 associations. Properties may point to primitiv types, reference codes or other data objects.
Indexed Properties are 1..* associations. They may only point to other data objects. To save persistent indexed properties a foreign key reference from the item to the container is added to the item table.
Modern client-server applications manipulate complex graphs of data objects. For example a contract with its positions and optional amendments is represented by a hierarchy of data information. These graphs are transferred from and to the client application. The user views data, edits it and commits the changes to the database through server functions. The patterns commonly found in the literature describe thoroughly how to implement simple data transfer objects (DTO) but ignore the complexity how graphs of objects should efficiently be transmitted between server and client components. The intended audience is software developers interested in using DOG or implementing their own variant.
A data object graph is represented by its root data object and the data object which are referenced by this root object.
The visitor pattern is used to traverse all data objects of a graph. I.e. if a root object has to be stored, the visitor is used to collects all data objects of this data object graph. For more information see here.
The PmMda.Net.Dog.Persistence.PersistenceHandler
is used to create, read,
update and delete (CRUD) data objects.
To create a data object, their default constructor is used. The identifier will be set
to null
to mark this data object as a new one. If the new data object is the
root object of the DOG you can call PersistenceHandler.Store(myNewDataObject);
to save the new object. An other possibility is to add the new data object to the root data
object or another data object which is references by the root object.
If the root object is stored (PersistenceHandler.Store(myRootDataObject);
),
the new data object will also be saved.
To read a root data object from the database the method
PersistenceHandler.Retrieve
is used. To retrive a data object, its identifier
and type must be specified. It is also possible to retrieve all data objects of a
specified type.
The update of a data object is done using the PersistenceHandler.Store
method. The Store
method calculates three lists:
This means the Store
method ensures that just the data of the modified
data object is stored. All other data is removed.
The PersistenceHandler.Delete
method deletes the specified root object
and all data objects which are referenced by the root objects.
In a multiuser environment, there are two models for updating persistent data:
pessimistic and optimistic concurrency.
Pessimistic concurrency involves locking rows at the data source to prevent users from
modifying data in a way that affects other users.
Optimistic concurrency does not lock a row when reading it. When a user wants to update a
row, the application must determine whether another user has changed the row since it was
read. Optimistic concurrency is generally used in environments with a low contention for
data.
pmMDA.NET does currently only support the optimistic concurrency scenario.
An update of a data object graph requires the following steps:
The storeage of the root object may fail if another user has updated the same data object while we were changing the data! In that case your changed data is lost.
Example of an optimistic concurrency violation:
Time | User 1 | User 2 |
---|---|---|
1 | Reads the data objects. | |
2 | Modifies the data objects. | |
3 | Reads the data objects. | |
4 | Stores the data objects | |
5 | Modifies the data objects | |
6 |
Stores the data objects --> The data objects were already modified by another transaction executed by user 1. The storange of the data objects will fail because of the optimistic concurrency violation and an exception is thrown. --> The modifications made by user 2 are lost. |
The same optimistic concurrency violation also occurs when user 1 would have deleted or
inserted data objects from or to the dog.
It is impossible that an optimistic concurrency violation occurs if the users modify
different data (i.e. different root objects) or if the users have only added new root
objects.
If paging is used it is possible that the optimistic concurrency violation is reported while the user reads or modifies data. For more information see the chapter Paging >> Error handling.
Further information on how the persistence of data object is implemented can be found in the design documentation.