Telerik OpenAccess Classic

Telerik OpenAccess ORM Send comments on this topic.
Walkthrough: Persistence by Reachability
Programmer's Guide > OpenAccess ORM Classic (Old API) > OpenAccess Tasks > Walkthrough: Persistence by Reachability

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.

This step explains how OpenAccess ORM supports persistence by reachability which, means that all objects that are reachable from a persistent class are automatically stored in the database (Refer to The OpenAccess ORM Object Lifecycle for more information). Aggregation of objects is integrated seamlessly into the C# and VB .NET programming language. This step also explains the load behavior for references to objects of persistence capable user classes.

The topic is based on an object-oriented data model. There is a Customer class as well as an Order class, which has a reference to one Customer and a Generic Collection of OrderDetail instances. Each OrderDetail references exactly one Product. The data model used has been explained diagrammatically below:

Data Model

Creating the persistent classes needed for Step 3

Order class where the usage of generic collections is demonstrated:

C# Copy Code
[OpenAccess.Persistent]
public class Order
{
   
// persistent reference to customer
   
// The customer field is aggregated in the Order class.
   
// Objects of Customer have their own identity
   
// and can be referenced by other objects as well.


   
private Customer customer;
   
private int orderNo;
   
private DateTime orderDate;
   
private DateTime shippedDate;


   
// Defining a Generic Collection of OrderDetail instances
   
private IList <OrderDetail> details;

   
public IList<OrderDetail> details
   {
       get
       {
           
if( details == null )
           {
               details =
new List<OrderDetail>();
           }
           
return details;
       }
   }
}
VB .NET Copy Code
<OpenAccess.Persistent> _
Public Class Order
 ' persistent reference to customer
 ' The customer field is aggregated in the Order class.
 ' Objects of Customer have their own identity
 ' and can be referenced by other objects as well.

 Private customer As Customer
 Private orderNo As Integer
 Private orderDate As DateTime
 Private shippedDate As DateTime

 ' Defining a Generic Collection of OrderDetail instances
 Private details As IList(Of OrderDetail)
 Public ReadOnly Property details() As IList(Of OrderDetail)
  Get
   If details = Nothing Then
    details = New List(Of OrderDetail)()
   End If
   Return details
  End Get
 End Property
End Class

Customer class where the usage of Nullables is demonstrated, for the discountRate field as shown below:

C# Copy Code
[OpenAccess.Persistent]
public class Customer
{
   
//the following members are private, but any access rights are permitted
   
private string name;
   
private int customerNo;
   
//Use of Nullable datatypes
   
private int? discountRate;
}        

 

VB .NET Copy Code
<OpenAccess.Persistent> _
Public Class Customer
 'the following members are private, but any access rights are permitted
 Private name As String
 Private customerNo As Integer
 'Use of Nullable datatypes
 Private discountRate As System.Nullable(Of Integer)
End Class

OrderDetail class

C# Copy Code
[OpenAccess.Persistent]
public class OrderDetail
{
   
private float unitPrice;
   
private int quantity;
   
// persistent reference to Product
   
private Product product;
}

 

VB .NET Copy Code
<OpenAccess.Persistent> _
Public Class OrderDetail
 Private unitPrice As Single
 Private quantity As Integer
 ' persistent reference to Product
 Private product As Product
End Class

Product class

C# Copy Code
[OpenAccess.Persistent]
public class Product : IInstanceCallbacks
{
   
private int productNo;
   
private string name;
   
private float unitPrice;
}
VB .NET Copy Code
<OpenAccess.Persistent> _
Public Class Product
 Implements IInstanceCallbacks
 Private productNo As Integer
 Private name As String
 Private unitPrice As Single
End Class

The class Product implements the methods of the OpenAccess.IInstanceCallbacks interface in order to obtain life cycle events. In this example, the PostLoad() method is used to print a message when the object data is retrieved, i.e., "loaded", from the database. A more practical example of using instance callbacks is for classes, which include non-persistent fields whose values depend on the values of persistent fields. The PostLoad() callback can be used to populate the values in these transient fields.

The PostLoad() is called after the values are loaded from the data store into this instance. Transient fields should be initialized in this method:

C# Copy Code
public void PostLoad();

 

VB .NET Copy Code
Public Sub PostLoad()

    Main Function

The main purpose of this task is to explain how to use the powerful concepts of persistence by reachability, aggregation of objects and delayed loading of complex object graphs.

C# Copy Code
static void Main(string[] args)
{
   IObjectScope scope = Database.Get(connectionId).GetObjectScope();

   Order order = CreateOrder(scope);

   DemonstrateDelayedLoading(scope, order);

   Console.WriteLine(
"\n\nPress <Return> to continue ...");
   Console.ReadLine();

   scope.Dispose();
}
VB .NET Copy Code
Shared Sub Main(args As String())
 Dim scope As IObjectScope = Database.Get(connectionId).GetObjectScope()
 Dim order As Order = CreateOrder(scope)
 DemonstrateDelayedLoading(scope, order)
 Console.WriteLine(vbLf & vbLf & "Press <Return> to continue ...")
 Console.ReadLine()
 scope.Dispose()
End Sub

Create a complex persistent Order object:

C# Copy Code
Order order = CreateOrder(scope);
VB .NET Copy Code
Dim order As Order = CreateOrder(scope)

The following method demonstrates the delayed loading of objects, i.e., how object references are loaded on demand:

C# Copy Code
DemonstrateDelayedLoading(scope, order);

 

VB .NET Copy Code
DemonstrateDelayedLoading(scope, order)

 

    Persistence by reachability

Given below is the definition of the CreateOrder class:

C# Copy Code
private static Order CreateOrder(IObjectScope scope)
{
   scope.Transaction.Begin();

   Product product1 =
new Product(1001, "Hamburger", 1.20f);
   Product product2 =
new Product(1002, "Cheeseburger", 1.30f);

   Customer customer =
new Customer("4567", "Smith");

   Order order =
new Order( customer );
   order.OrderNo = 100546;
   order.OrderDate =
new DateTime(2004, 02, 01);
   order.ShippedDate =
new DateTime(2004, 02, 01);
   order.Details.Add(
new OrderDetail(1.20f, 2, product1));
   order.Details.Add(
new OrderDetail(1.30f, 3, product2));

   scope.Add(order);

   scope.Transaction.Commit();

   
return order;
}

 

VB .NET Copy Code
Private Shared Function CreateOrder(scope As IObjectScope) As Order
 scope.Transaction.Begin()
 Dim product1 As New Product(1001, "Hamburger", 1.2F)
 Dim product2 As New Product(1002, "Cheeseburger", 1.3F)
 Dim customer As New Customer("4567", "Smith")
 Dim order As New Order(customer)
 order.OrderNo = 100546
 order.OrderDate = New DateTime(2004, 2, 1)
 order.ShippedDate = New DateTime(2004, 2, 1)
 order.Details.Add(New OrderDetail(1.2F, 2, product1))
 order.Details.Add(New OrderDetail(1.3F, 3, product2))
 scope.Add(order)
 scope.Transaction.Commit()
 Return order
End Function

The following method calls the Product class constructor to set the fields productNo, name, and unitPrice:

C# Copy Code
Product product1 = new Product(1001, "Hamburger", 1.20f);

 

VB .NET Copy Code
Dim product1 As New Product(1001, "Hamburger", 1.2F)

Creating an instance of Order with the customer as parameter:

C# Copy Code
Order order = new Order( customer );

 

VB .NET Copy Code
Dim order As New Order(customer)

Adding an OrderDetail instance to the Details ArrayList/Generic Collection field of the Order object:

C# Copy Code
order.Details.Add(new OrderDetail(1.20f, 2, product1));

 

VB .NET Copy Code
order.Details.Add(New OrderDetail(1.2F, 2, product1))

Adding the order object to the scope implicitly adds all the objects referenced by order to the scope as well. This powerful concept is called persistence by reachability. The entire object graph will be stored in the database during the following Commit() call:

C# Copy Code
scope.Add(order);
VB .NET Copy Code
scope.Add(order)

   Delayed loading of objects

C# Copy Code
private static void DemonstrateDelayedLoading(IObjectScope scope, Order order)
{
   scope.Transaction.Begin();

   Console.WriteLine(
"The orders number is: {0}", order.OrderNo);

   Console.WriteLine(
"Name of first ordered product: {0}",
       order.Details[0].Product.Name);

   scope.Transaction.Commit();
}
VB .NET Copy Code
Private Shared Sub DemonstrateDelayedLoading(scope As IObjectScope, order As Order)
 scope.Transaction.Begin()
 Console.WriteLine("The orders number is: {0}", order.OrderNo)
 Console.WriteLine("Name of first ordered product: {0}",
 order.Details(0).Product.Name)
 scope.Transaction.Commit()
End Sub

OpenAccess ORM automatically ensures, that if the Order object is accessed in a new transaction, then its values are loaded from the database again upon the first access to the object. The Visual Studio debugger also loads the objects into memory when the object values need to be displayed. For example, expanding all object references from order triggers the loading of objects from the database.

The following code fragment begins a transaction:

C# Copy Code
scope.Transaction.Begin();
VB .NET Copy Code
scope.Transaction.Begin()

The following code fragment loads the Order object:

C# Copy Code
Console.WriteLine("The orders number is: {0}", order.OrderNo);
VB .NET Copy Code
Console.WriteLine("The orders number is: {0}", order.OrderNo)

The access to the field order.orderNo (via property OrderNo ) triggers the loading of the default fetch group for the Order object from the database. By default, the default fetch group contains all value type fields (e.g. Int32, Datetime, and user-defined structs) and string fields. The default FetchGroup also contains only the id of the reference fields, the referenced object itself will only be fetched when it is actually accessed. You can modify the default fetch group for a class using the DefaultFetchGroupAttribute field attribute. Fields, which are not present in the default fetch group are loaded on demand, when they are accessed for the first time. This includes references to persistent classes, such as the customer field, and collections, such as the details field. For example, calling the customer.Name property requires the Customer reference in the Order to be resolved. Refer to The OpenAccess ORM Object Lifecycle for more information about object resolution and delayed loading.

The following code fragment loads the product objects:

C# Copy Code
order.Details[0].Product.Name;
VB .NET Copy Code
order.Details(0).Product.Name

Here, the name of the product of the first OrderDetail object is accessed. Access to the order.Details property triggers loading of the details field, and access to the Product.Name property triggers the loading of one Product object including its default fetch group fields. This is demonstrated by printing a message to the console in the PostLoad() method of the Product class:

C# Copy Code
[OpenAccess.Persistent]
public class Product : IInstanceCallbacks
{
 
public void PostLoad()
 {
     Console.WriteLine(
"A Product instance has been loaded from the database.");
 }
}
VB .NET Copy Code
<OpenAccess.Persistent> _
Public Class Product
 Implements IInstanceCallbacks
 Public Sub PostLoad()
   Console.WriteLine("A Product instance has been loaded from the database.")
 End Sub
End Class

PostLoad() is called after the default fetch group values are loaded in the persistent instance. This is not done inside the query execution. The fields are loaded into the instance, either when you access a default fetch group field or when you call retrieve(). Inside the PostLoad call, only fields of the default fetch group are allowed to be accessed; other fields may not be set. It is only allowed to change transient fields from within the PostLoad, for e.g., Overriding PostLoad() is useful for initializing transient fields.

For more information about the IInstanceCallbacks interface and life cycle events, refer to The OpenAccess ORM Object Lifecycle.