Telerik OpenAccess Classic

Telerik OpenAccess ORM Send comments on this topic.
How to: Implement IInstanceCallbacks and IInitializeTransients
Programmer's Guide > OpenAccess ORM Classic (Old API) > OpenAccess Tasks > Working with Objects > How to: Implement IInstanceCallbacks and IInitializeTransients

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.

For the purposes of this example two sample classes will be forward mapped.

Class Person:

C# Copy Code
[Telerik.OpenAccess.Persistent(IdentityField = "personID")]
  
public class Person
   {
       
public Person() { }
       
private int personID;
       [Telerik.OpenAccess.FieldAlias(
"personID")]
       
public int PersonID
       {
           get {
return personID; }
           set { personID = value; }
       }
       
private string personName;
       [Telerik.OpenAccess.FieldAlias(
"personName")]
       
public string PersonName
       {
           get {
return personName; }
           set { personName = value; }
       }
       
private DateTime birthday;
       [Telerik.OpenAccess.FieldAlias(
"birthday")]
       
public DateTime Birthday
       {
           get {
return birthday; }
           set { birthday = value; }
       }
       
private string additionalInfo;
       [Telerik.OpenAccess.FieldAlias(
"additionalInfo")]
       
public string AdditionalInfo
       {
           get {
return additionalInfo; }
           set { additionalInfo = value; }
       }
       
private Address personAddress;
       [Telerik.OpenAccess.FieldAlias(
"personAddress")]
       
public Address PersonAddress
       {
           get {
return personAddress; }
           set { personAddress = value; }
       }
       [Telerik.OpenAccess.Transient()]
       
private int years;
       
public int Years
       {
           get {
return years; }
           set { years = value; }
       }
public override string ToString()
       {
           
return String.Format("person name: {0}\nperson address: {1}\nperson age: {2}\n", PersonName, PersonAddress.ToString(), Years);
       }
VB.NET Copy Code
  <Telerik.OpenAccess.Persistent(IdentityField:="personID")> _
 Public Class Person
        Public Sub New()
        End Sub
        Private personID As Integer
        <Telerik.OpenAccess.FieldAlias("personID")> _
        Public Property PersonID_Renamed() As Integer
            Get
                Return personID
            End Get
            Set(ByVal value As Integer)
                personID = value
            End Set
        End Property
        Private personName As String
        <Telerik.OpenAccess.FieldAlias("personName")> _
        Public Property PersonName_Renamed() As String
            Get
                Return personName
            End Get
            Set(ByVal value As String)
                personName = value
            End Set
        End Property
        Private birthday As DateTime
        <Telerik.OpenAccess.FieldAlias("birthday")> _
        Public Property Birthday_Renamed() As DateTime
            Get
                Return birthday
            End Get
            Set(ByVal value As DateTime)
                birthday = value
            End Set
        End Property
        Private additionalInfo As String
        <Telerik.OpenAccess.FieldAlias("additionalInfo")> _
        Public Property AdditionalInfo_Renamed() As String
            Get
                Return additionalInfo
            End Get
            Set(ByVal value As String)
                additionalInfo = value
            End Set
        End Property
        Private personAddress As Address
        <Telerik.OpenAccess.FieldAlias("personAddress")> _
        Public Property PersonAddress_Renamed() As Address
            Get
                Return personAddress
            End Get
            Set(ByVal value As Address)
                personAddress = value
            End Set
        End Property
        <Telerik.OpenAccess.Transient()> _
        Private years As Integer
        Public Property Years_Renamed() As Integer
            Get
                Return years
            End Get
            Set(ByVal value As Integer)
                years = value
            End Set
        End Property

Class Address:

C# Copy Code
public class Address
   {
       
public Address()
       {
       }
       
private int addressId;
       [Telerik.OpenAccess.FieldAlias(
"addressId")]
       
public int AddressId
       {
           get {
return addressId; }
           set { addressId = value; }
       }
       
private string city;
       [Telerik.OpenAccess.FieldAlias(
"city")]
       
public string City
       {
           get {
return city; }
           set { city = value; }
       }
       
private string country;
       [Telerik.OpenAccess.FieldAlias(
"country")]
       
public string Country
       {
           get {
return country; }
           set { country = value; }
       }
       
private string postal;
       [Telerik.OpenAccess.FieldAlias(
"postal")]
       
public string Postal
       {
           get {
return postal; }
           set { postal = value; }
       }
       
public override string ToString()
       {
           
return String.Format("\ncity: {0}\ncountry: {1}",City,Country);
       }
VB.NET Copy Code
  <Telerik.OpenAccess.Persistent(IdentityField:="addressId")> _
 Public Class Address
        Public Sub New()
        End Sub
        Private addressId As Integer
        <Telerik.OpenAccess.FieldAlias("addressId")> _
        Public Property AddressId_Renamed() As Integer
            Get
                Return addressId
            End Get
            Set(ByVal value As Integer)
                addressId = value
            End Set
        End Property
        Private city As String
        <Telerik.OpenAccess.FieldAlias("city")> _
        Public Property City_Renamed() As String
            Get
                Return city
            End Get
            Set(ByVal value As String)
                city = value
            End Set
        End Property
        Private country As String
        <Telerik.OpenAccess.FieldAlias("country")> _
        Public Property Country_Renamed() As String
            Get
                Return country
            End Get
            Set(ByVal value As String)
                country = value
            End Set
        End Property
        Private postal As String
        <Telerik.OpenAccess.FieldAlias("postal")> _
        Public Property Postal_Renamed() As String
            Get
                Return postal
            End Get
            Set(ByVal value As String)
                postal = value
            End Set
        End Property
        Public Overrides Function ToString() As String
            Return String.Format(Constants.vbLf & "city: {0}" & Constants.vbLf & "country: {1}", City, Country)
        End Function

Implementing IinstanceCallbacks Interface

OpenAccess ORM can automatically perform application-specific operations on class objects during certain lifecycle events. By implementing the IInstanceCallbacks interface for a persistence capable class, user-defined methods are called when an object of the class is read from the database, when an object is removed (deleted) from the database, or when an object is written to the database. The IInstanceCallbacks interface methods are, respectively, PostLoad(), PreDelete(), and PreStore(). In order for this interface to be fully working you will need to implement all the three methods in your class. For the purposes of this example the Person class will inherit the IinstanceCallBacks interface.

C# Copy Code
public class Person:IInstanceCallbacks
VB.NET Copy Code
Public Class Person
        Implements IInstanceCallbacks, IInitializeTransients
  • PostLoad()

The PostLoad() method is called when the object is resolved, i.e., the object data is loaded from the database into memory. OpenAccess ORM uses delayed loading. Even if you have a reference to the object, the object data is not read from the database until some operation that requires the data is performed on the object. Only at this time is the object resolved. This means that the PostLoad() method is not necessarily called when you first get a reference to the object but rather when the object data is requested

The PostLoad() method is particularly useful for initializing any transient fields whose values depend on persistent field values. For the purpose of this example we have a Person class that has a transient field for the person's age, this can be calculated from the persistent birthdate field (and the current date) when a Person object is resolved.

C# Copy Code
public void PostLoad()
       {
           
//the post load method is fired when the first persistent property of the Person class is accessed
           
years = CalculateYears(Birthday, PersonName);
       }
private static int CalculateYears(DateTime Birthday,string personName)
       {
           
int years = 0;
           
if (DateTime.Today.Month == Birthday.Month)
           {
               
if (DateTime.Today.Day.CompareTo(Birthday.Day) < 0)
               {
                   years = DateTime.Today.Year - Birthday.Year - 1;
               }
               
else if (DateTime.Today.Day.CompareTo(Birthday.Day) > 0)
               {
                   years = DateTime.Today.Year - Birthday.Year;
               }
               
else
               {
                   
Console.WriteLine("Today is {0}'s birthday", personName);
                   years = DateTime.Today.Year - Birthday.Year;
               }
           }
           
else if (DateTime.Today.Month.CompareTo(Birthday.Month) < 0)
           {
               years = DateTime.Today.Year - Birthday.Year - 1;
           }
           
else
           {
               
years = DateTime.Today.Year - Birthday.Year;
           }
           
return years;
       }
VB.NET Copy Code
 Public Sub PostLoad() Implements IInstanceCallbacks.PostLoad
            'the post load method is fired when the first persistent property of the Person class is accessed
            years_Renamed = CalculateYears(Birthday, PersonName)
        End Sub
 'calculates the acctual ages of the person
        Private Shared Function CalculateYears(ByVal Birthday As DateTime, ByVal personName As String) As Integer
            Dim years As Integer = 0
            If DateTime.Today.Month = Birthday.Month Then
                If DateTime.Today.Day.CompareTo(Birthday.Day) < 0 Then
                    years = DateTime.Today.Year - Birthday.Year - 1
                ElseIf DateTime.Today.Day.CompareTo(Birthday.Day) > 0 Then
                    years = DateTime.Today.Year - Birthday.Year
                Else
                    Console.WriteLine("Today is {0}'s birthday", personName)
                    years = DateTime.Today.Year - Birthday.Year
                End If
            ElseIf DateTime.Today.Month.CompareTo(Birthday.Month) < 0 Then
                years = DateTime.Today.Year - Birthday.Year - 1
            Else
                years = DateTime.Today.Year - Birthday.Year
            End If
            Return years
        End Function
The above code will generate the age of the person each time a person object is loaded.
  • PreRemove (IObjectScope scope)

This method is called in the IObjectContext.Remove() method, i.e., when the object is removed (deleted) from the database (IObjectContext is the base interface for IObjectScope and ObjectContainer. The parameter scope can be used to propagate deletion to child objects. This propagation can also be configured in the mapping data. In this example we will use the PreRemove for the Person class. Each time a person is removed we will check if the address for this person is used by someone else and if not the address will be removed as well.

C# Copy Code
public void PreRemove(IObjectScope objectScope)
       {
           
//when the Person object is being removed this event is fired
           
//the following code checks if the address from which the person is points to another person
           
//and if not it removes the address as well.
           
int addressID = this.PersonAddress.AddressId;
           var result = from c
in objectScope.Extent<Person>() where c.PersonAddress.AddressId == addressID select c;
           
if (result.Count() == 1)
           {
               objectScope.Remove(objectScope.Extent<Address>().Where(c => c.AddressId == addressID));
           }
       }
VB.NET Copy Code
Public Sub PreRemove(ByVal objectScope As IObjectScope) Implements IInstanceCallbacks.PreRemove
            'when the Person object is being removed this event is fired
            'the following code checks if the address from which the person is points to another person
            'and if not it removes the address as well.
            Dim addressID As Integer = Me.personAddress.AddressId_Renamed
            Dim result = From c In objectScope.Extent(Of Person)() _
                         Where c.personAddress.AddressId_Renamed = addressID _
                         Select c
            If result.Count() = 1 Then
                objectScope.Remove(objectScope.Extent(Of Address)().Where(Function(c) c.AddressId_Renamed = addressID))
            End If
        End Sub
The PreRemove is called before the transaction is commited so you don’t have to open a new transaction for deleting the address.
  • PreStore()

This method is called when a person object is written to the database during the transaction Commit() or Checkpoint() call. The PreStore() method is called before the object is actually written to the database. You could, for example, update persistent fields based on transient data, or, based on the results of any operations performed during the PreStore() call, actions such as throwing an exception and aborting the transaction may be taken. For the purpose of this example we will precalculate the persons age and if the persons age is less than 18 than 2 choices will be given:

  1. Set a default birthdate for the new/modified person
  2. Abort the transaction.
C# Copy Code
public void PreStore()
       {
           years = CalculateYears(Birthday, PersonName);
           
if (years < 18)
           {
               Console.WriteLine(
"Do you want to set the default birthdate(12/12/1988)?\ny/n");
               
char k =char.Parse(Console.ReadLine().ToString());
               
switch (k)
               {
                   
case 'y' : birthday = DateTime.Parse("12/12/1987"); break;
                   
case 'n': throw new Exception("Transaction stopped");
               }
           }
       }
VB.NET Copy Code
 Public Sub PreStore() Implements IInstanceCallbacks.PreStore
            years_Renamed = CalculateYears(Birthday_Renamed, PersonName_Renamed)
            If years < 18 Then
                Console.WriteLine("Do you want to set the default birthdate(12/12/1988)?" & Constants.vbLf & "y/n")
                Dim k As Char = Char.Parse(Console.ReadLine().ToString())
                Select Case k
                    Case "y"c
                        birthday = DateTime.Parse("12/12/1987")
                    Case "n"c
                        Throw New Exception("Transaction stopped")
                End Select
            End If
        End Sub

Implementing IinitializeTransients Interface

Sometimes we might want to initziate our transients earlier than the PostLoad() event is fired. For example when we retrieve an object by its ID we are creating only the object reference but we are not acessing any field. In such case the transient fields wont be initzialized because no transient field has been accessed. In such cases we can use the IinitializeTransients interface.

C# Copy Code
Person prs =(Person) scope.GetObjectById(Database.OID.ParseObjectId(typeof(Person),"2"));
VB.NET Copy Code
Dim prs As Person =CType(scope.GetObjectById(Database.OID.ParseObjectId(GetType(Person),"2")), Person)

We will need to implement its only method InitializeTransients(InitOperation initOperation). Note that we need extra care for this method as it can be fired for many occasions. For example when we create an object the method will be fired with initOperation equaling “Constructed”. The InitializeTransients will be fired for Refresh operations and even for transation.Begin() with the state of the object being Hallow. In order not to become resource consuming this method needs to be taken care of properly. In our example we will use the InitializeTransients to initialize our age property only on construction.

C# Copy Code
public void InitializeTransients(InitOperation initOperation)
       {
           
if(InitOperation.Constructed.Equals(initOperation))
           {
               years = CalculateYears(Birthday, PersonName);
           }
       }
VB.NET Copy Code
Public Sub InitializeTransients(ByVal initOperation As InitOperation) Implements IInitializeTransients.InitializeTransients
            If initOperation.Constructed.Equals(initOperation) Then
                years = CalculateYears(Birthday_Renamed, PersonName_Renamed)
            End If
        End Sub

The InitializeTransients can be fired with the following initOperations :

Apply

when the information from a changeset is being applied. 

Constructed

when the object is being constructed.

CopyFrom

when a object is being copied to a container

Hollow

when the object is in hollow state(when transaction.begin()) is called

Refresh

when the object is being refreshed using scope.Refresh();

Retrieve

when the object is being retrieved using the scope.Retrieve();