Telerik OpenAccess Classic

Telerik OpenAccess ORM Send comments on this topic.
How to: Return a Specific Object Using its Key
Programmer's Guide > OpenAccess ORM Classic (Old API) > OpenAccess Tasks > Working with Objects > How to: Return a Specific Object Using its Key

Glossary Item Box

This documentation article is a legacy resource describing the functionality of the deprecated OpenAccess Classic only. The contemporary documentation of Telerik OpenAccess ORM is available here.

When persisting objects, the objects require an "identity", so as to uniquely identify them for storage and retrieval. This in-memory id is normally mapped directly to a primary key column or columns in the database.
Every object in an OpenAccess ORM database has a unique identifier, i.e. a "primary key" from the database point of view. The identifier has to be defined when the object is made persistent (except when a key generator mechanism is used). Using this identifier, you can directly retrieve an object from the database.

The IObjectScope.GetObjectId() method can be used to get the identifier of an object. This method returns an instance of a class that implements the IObjectId interface:

C# Copy Code
scope.Transaction.Begin();
Customer c = new Customer();
c.Name =
"Smith";
c.CustomerNo = 34567;
scope.Add( c );
// during the Add() call, an object identifier
// is assigned to the new Customer object
IObjectId id = scope.GetObjectId( c );
scope.Transaction.Commit();
VB .NET Copy Code
scope.Transaction.Begin()
Dim c As New Customer()
c.Name = "Smith"
c.CustomerNo = 34567
scope.Add(c)
' during the Add() call, an object identifier
' is assigned to the new Customer object
Dim id As IObjectId = scope.GetObjectId(c)
scope.Transaction.Commit()

You can use the object identifier to directly retrieve an object from the database, perhaps in a later transaction:

C# Copy Code
scope.Transaction.Begin();
Customer c = (Customer) scope.GetObjectById( id );
VB .NET Copy Code
scope.Transaction.Begin()
Dim c As Customer = DirectCast(scope.GetObjectById(id), Customer)

You can also use the string representation of the object identifier to retrieve objects. Using the string representation of the object identifier allows you to easily transfer the object identity from one application to another, for example. To get the string representation of the identifier, use the ToString() method:

C# Copy Code
IObjectId id = scope.GetObjectId( c );
string idString = id.ToString();
VB .NET Copy Code
Dim id As IObjectId = scope.GetObjectId(c)
Dim idString As String = id.ToString()

You can create object identifiers by parsing a string representation for a given type, by using the Database.OID class method ParseObjectId( typeOf( TargetClassName ), string idString ). For internal identity, you can also pass null as first parameter. Note that the identity string has a specific format:

C# Copy Code
Customer c = (Customer) scope.GetObjectById( Database.OID.ParseObjectId(null, scope.Database.GetClassId(typeof(Customer)).ToString() + "-" + idString));
VB .NET Copy Code
Dim c As Customer = DirectCast(scope.GetObjectById(Database.OID.ParseObjectId(Nothing, scope.Database.GetClassId( GetType(Customer)).ToString() & "-" & idString)), Customer)

When it is required to pass the type of the referenced persistent instance along with it's keys, you must use the Database.OID.ToString(IObjectId) and Database.OID.FromString(string) method pair to transform between IObjectId and string. This pair does not require that the type is statically known. The Database.OID.GetObjectId() method returns the object identity for the passed object. This method is especially useful for getting the object identity when single or multiple field identity is used and the object is not connected. It returns the correct object identity (even for internal identity), in the connected mode as well.

 

How does scope.GetObjectById() work

When calling IObjectScope.GetObjectById(IObjectId), an attempt is made to find the in-memory representation associated with the given object identity. If there is already an object associated with the passed IObjectId in memory, the method quickly returns this instance without additional database fetches. This can return the instance in a “hollow” state where only the (user visible) identity fields are loaded; other states like “persistent-clean”, “dirty” or “deleted” are also possible depending on the current state of the instance.


If no instance is present, the runtime tries to find already loaded data for the given identity. Data of the object can already be present in the scope by having an appropriate fetch plan. When such data is present, the user visible in-memory representation is created from that data and returned to the caller in a “persistent-clean” state.


In cases where no in-memory representation is already available and no data has been pre-fetched, the database server is contacted to obtain the values of the default fetch group for that instance. This verifies also that there is a persistent instance for the given IObjectId (could already be deleted too!). Using the fetched data, an instance of the correct type is created, its default fetch group fields are populated and the instance is returned to the caller in a “persistent-clean” state.

 

NOTE: Loading all the values for the primitive type fields is a behavior of the default fetch group. This can be changed by using user-defined fetch groups (FetchGroup attribute), explicit DefaultFetchGroup attributes and altering the current fetch plan.


After the call to the database is made the state of the desired instance is “persistent-clean” and all the fetched values are now stored in the 1st level cache. If the user application asks for another field of the instance returned its value is loaded from the 1st level cache is present there; otherwise the fetch groups of the accessed field are fetched from the server.


What is the behavior when the 2nd level cache is enabled:

After objects values are first initialized with a call to the database the 2nd level cache is also populated with them like the 1st level cache. The difference is that on the start of a transaction the 1st level cache is cleaned and all the persistent instances get into a “hollow” state. That is not the case with the 2nd level cache though where the data of the persistent capable objects is kept in a “persistent clean” state. If the data of the requested object from scope.GetObjectById() is not available in the 1st level cache the runtime tries to find it  in the 2nd level cache.


In a case that a field of a persistent class is modified in a transaction it gets to the state “persistent-dirty”. After the scope.Transaction.Commit() the data of the objects with that state are removed from the 2nd level cache in order to avoid inconsistent values.

Retrieving the class id of a persistent type

OpenAccess ORM provides you with a method that takes a persistent type returns its class id. It is located at the database instance. You also can call it from the ObjectScope instance, for e.g.:

C# Copy Code
scope.Database.GetClassId(typeof(Person));
VB .NET Copy Code
scope.Database.GetClassId(GetType(Person))

This will return a class id for a specific type. In the above example the class id for the 'Person' type will be returned.