- One - To - One (one row to one row)
- One - To Many (one row to many rows)
- Many - To -One (many rows to one row. Inverse of the One - To Many)
- Many - To - Many (Many rows to Many rows).
So, to maximize the usage of data when we are mapping relational to object oriented, we need to use a pattern called "Lazy Loading".
What is Lazy Loading.
Simply, lazy loading can be described as an approach for loading objects or data when we need them. This helps to save CPU cycles because we do not intend to load what we do not need, thereby we free our heap from unessary usage, and the almighty deallocator, the Gargbage collector can go to sleep. Simple lazy loading example can be described as follows :
public IList Customers
if(null == customer)
customers = LoadCustomers();
From the above code snippet, we are checking for null values and if lists of customers is not null, we load the already loaded lists of customers; else we load new ones. This is a classic lazy loading feature. This kind of lazy loading will still load all customers, except a stub that refers to the list that contains the data is returned. But in the case of the classic lazy-loading, we are loading everything and returning everything. (Thats Bad, isn't it).
The Concept of Laziest Loading
Let us assume the following relationships between two objects, Customer has many Orders . The relationships that we described above is a One - To - Many scenairo, One customer to many orders, the following class diagram and code depicts the relationship :
The relationships in the diagram says one instance on a customer class will have multiple instances of an order in its relationship bag. And one instance of an order, will have one instance of a customer class. Thats it.
The Perfect Solution
In the classic lazy loading scenario, we are loading everything once it is required, even if we do not intend to use everything in the list, we end up fetching objects into the list which they will all be allocated memory space on the heap. unwanted objects made its way to the CLR heap, thats not what we bargained for.
What if we create our own special list and a class that implements IEnumerator for a special case of doing the laziest loading. The following diagram depicts our new concepts of laziest loading of data structure using our custom built list (implements from IList) and custom iterator, inplements IEnumerator : The diagram below represents the concept of the laziest loading and relational to object transformation, using datareader and our custom enumerator class.
Datareader mapping with Custom Enumerator
When you create a custom enumerator, you will implement the IEnumerator interface, which will give you various method and properties to override, but in this article, i will be talking about three methods that are very useful in our lazy loading scenario. The idea is to lazily load objects from the database when they are iterated instead of when the whole list is called. For that to happen, we need an open data reader that we need to read when the GetEnumerator method of the list that use's this Enumerator is called.
Enumerator MoveNext Mapped to DataReader Read
When there is a call to our List (Probably you need to create a custom list that implements IList, and returns our custom Enumerator). When in a froeach loop, an iterator can know if it has data to iterate with the call to movenext method. And since we are fetching from the database, we need to call the Read method of our DataReader here, this will ensure that our dataReader moves it cursor to the next row in the list. This can also return false to indicate that there is no more row to read.
public bool MoveNext()
Current and DataReader Get[Type]
Enumerator Current property used to translate DataReader Get[DataType] to our domain or entity object. For example, let us look at the code snippet below :
public object Current
ProductOrder order = new ProductOrder();
order.OrderDate = Convert.ToDateTime(dataReader);
order.OrderName = Convert.ToString(dataReader);
Enumerator Dispose Method (If you implement IDisposable)
The disposed method is fired when you exit the foreach loop, or you read the list to the end, or you break from the list. It is a wise idea to close the datareader in the dispose section, and before you close the datareader, it is wise to keep moving the datareader till it gets to its last (In case the loop was exited with break statement). The connection shouldnt be close here, because you may be using it for other database operations (Especially if you are using MARS (Multiple Active Result Sets) new feature in "SQL 2005 YUKON". The following code snippet, depicts the kind of stuffs that you can do with the dispose method :
public void Dispose()
//Do not do anything again here. This is called when you exit the loop
//By end of loop or break statement.
This is just the beginning, so watch out for part two when we create lazy loading handlers delegates and when i introduce you all to Ghosting or Dynamic Proxy in C# using IL (Intermediate Language). Thanks.
You can now find the Dynamic Proxy Part 1 here