Quantcast
Channel: TMS Software
Viewing all articles
Browse latest Browse all 1006

My Top 10 Aurelius Features - #8 Lazy Loading

$
0
0

The ability to lazy-load an association is placed at number 8 in the list of My Top 10 Aurelius Features.


Suppose you have a TContact class like the following:

type
  TContact = class
  private
    FId: integer;
    FName: string;
    FCountry: TCountry;
  public
    property Id: integer read FId write FId;
    property Name: string read FName write FName;
    property Country: TCountry read FCountry write FCountry;
  end;
And you use the following code to retrieve a list of contacts and get the name of the country of the first contact (to reduce size, the code doesn't check for potential errors or release memory):
// Get all contacts
MyContacts := Manager.Find<TContact>.List;

// Get name of country of first contact:
FirstContactCountryName := MyContacts[0].Country.Name;
By default, when the first line is executed, Aurelius builds and executes an SQL statement retrieving column values from Contact table and Country table, all at once, and all entities (contacts and countries) are created and instantiated:
SELECT A.ID AS A_ID, A.NAME AS A_NAME, A.COUNTRY_ID AS A_COUNTRY_ID, B.ID AS B_ID, B.NAME AS B_NAME
FROM CONTACT A LEFT JOIN COUNTRY B ON (B.ID = A.COUNTRY_ID)
When the second line is executed (when we retrieve the Country name), all data is already in memory. This is useful in many situations, but there might be cases where doing several LEFT JOIN for many associations and retrieving data for all records is not desired. If I'm not going to get the country name for all contacts I retrieve, for example, why would I want to retrieve all country names in advance?

The lazy-loading feature gives you that flexibility. You just reimplement your class using a special Proxy type:
type
  TContact = class
  private
    FId: integer;
    FName: string;
    FCountry: Proxy<TCountry>;
    function GetCountry: TCountry;
    procedure SetCountry(const Value: TCountry);
  public
    property Id: integer read FId write FId;
    property Name: string read FName write FName;
    property Country: TCountry read GetCountry write SetCountry;
  end;

function TContact.GetCountry: TCountry;
begin
  Result := FCountry.Value;
end;

procedure TContact.SetCountry(const Value: TCountry);
begin
  FCountry.Value := Value;
end;
If we execute the code that retrieve contacts again, this is the SQL executed by Aurelius:
SELECT A.ID AS A_ID, A.NAME AS A_NAME, A.COUNTRY_ID AS A_COUNTRY_ID 
FROM CONTACT A
No data from country was retrieved. Only when the second line is executed, Aurelius executes an extra SQL statement:
SELECT A.ID AS A_ID, A.NAME AS A_NAME
FROM COUNTRY A
and then the country object is populated. Now you have the two options and you can fine tune your application for the best situation. It's also worth note that even when you have mapped your class to lazy-load a specific association, in some queries you can force Aurelius to still load all objects at once. The following code will use LEFT JOIN and retrieve all countries in the same SQL statement:
// Get all contacts and countries at once,
// regardless of lazy-load mode in mapping
MyContacts := Manager.Find<TContact>
  .CreateAlias('Country', 'c', TFetchMode.Eager)
  .List;

// Get name of country of first contact:
FirstContactCountryName := MyContacts[0].Country.Name;
Don't forget to subscribe to our YouTube channel and receive notifications about upcoming videos!



Viewing all articles
Browse latest Browse all 1006

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>