Lazy loading
Once loaded, the entity is not loaded again, if you access the property again. This is called load on demand or lazy loading: the load action of the related entity (in our example 'customer') is done when you ask for it, not when the referencing entity (in our example 'order') is loaded.
You can set a flag which makes the code load the related entity each time you access the property: entity.AlwaysFetchFieldMappedOnRelation. In our example of Order and Customer, OrderEntity has a property called AlwaysFetchCustomer and CustomerEntity has a property called AlwaysFetchOrders. Default for these properties is 'false'.
Setting these properties to true, will assure that the related entity is reloaded from the database each time you access the property. This can be handy if you want to stay up to date with the related entity state in the database. It can degrade performance, so use the property with care.
Another way to force loading of a related entity or collection is by specifying true for the forceFetch parameter in the GetSingleFieldMappedOnRelation call, or when the property contains a collection, GetMultiFieldMappedOnRelation call. Forcing a fetch has a difference with AlwaysFetchFieldMappedOnRelation in that a forced fetch will clear the collection first, while AlwaysFetchFieldMappedOnRelation does not. A forced fetch will thus remove new entities added to the collection from that collection as these are not yet stored in the database.
If you use a prefetch path to read a Customer and its related Order entities from the database, the Orders will not be re-loaded if you access the property after the fetch. A prefetch path which loads related entities makes sure that lazy loading will not undo the work the prefetch path already performed.
When the related entity is not found in the database, for example Customer has an optional relation with Address using Customer.VisitingAddressID - Address.AddressID and myCustomer.VisitingAddress is accessed and myCustomer doesn't have a related visiting address entity, by default the generated code will return null (Nothing).
You can make the entity to return a new instance instead of null if the entity is not found by setting the property FieldMappedOnRelationReturnNewIfNotFound to true. In our example of the Customer and its optional VisitingAddress field, mapped on the relation Customer.VisitingAddressID - Address.AddressID, Customer will have a property VisitingAddressReturnNewIfNotFound. Setting this property to true will make myCustomer.VisitingAddress return a new instance if the related Address entity is not found for myCustomer.
If the FieldMappedOnRelationReturnNewIfNotFound property is set to true and a new, empty entity is returned, in the example a new AddressEntity instance, you can then test the entity.Fields.State value of the returned entity if it is a new entity or a fetched entity (by comparing the Fields.State property with EntityState.New for a new entity or EntityState.Fetched for a fetched entity).
By default the FieldMappedOnRelationReturnNewIfNotFound properties are set to false. You can change this default in the LLBLGen Pro designer: in the project settings, change the project setting LazyLoadingWithoutResultReturnsNew in the LLBLGen Pro Runtime Framework subsection, to true and re-generate your code. The code generator will now generate 'true' / True' for all FieldMappedOnRelationReturnNewIfNotFound flags in all entities which will make sure that if an entity doesn't exist, null / Nothing is returned instead of a new entity.
If a related entity isn't found and you have set 'LazyLoadingWithoutResultReturnsNew' to true in the project settings, the entity instance returned by the navigator is empty and therefore reading the navigator again will return a different instance, as the new, empty entity isn't seen as a result of the Lazy Loading query.
Be aware that some code can trigger lazy loading while you didn't intent to. Consider Customer and Order which have an 1:n relation (and Order and Customer have a m:1 relation). The following code triggers the fetch of all orders for the myCustomer instance, while that wasn't the intention:
myCustomer.Orders.Add(myOrder);
while this code:
myOrder.Customer = myCustomer;
does the same thing, as LLBLGen Pro keeps both sides of a relation in sync, however this line of code doesn't trigger lazy loading.
How to switch off lazy loading completely
Lazy loading isn't without downsides, e.g. the SELECT N+1 problem is a real issue with lazy loading where related data is fetched unintented, e.g. in a loop. It might therefore be beneficial to switch off lazy loading completely to avoid running into the downsides of lazy loading.
You can do that by setting the global boolean property EntityBase.EnableLazyLoading
to false. By default this property is set to true
for backwards compatibility (as lazy loading is on by default in SelfServicing). Setting the properties entity.AlwaysFetchFieldMappedOnRelation, e.g.
myOrder.AlwaysFetchCustomer
to true will override this setting. Also passing true for forceFetch
in the methods which implement the
lazy loading feature, the entity.GetSingle/MultiNavigator methods will override this global property.
If you have the setting LazyLoadingWithoutResultReturnsNew set to true in the LLBLGen Pro Runtime Framework settings in the designer, the property EntityBase.EnableLazyLoading
controls the fetch of related data only. So when the property EntityBase.EnableLazyLoading
is set to false (so no lazy loading occurs), accessing a navigator property, e.g. myOrder.Customer
, still returns a new entity if the setting LazyLoadingWithoutResultReturnsNew is set to true.
How to intercept lazy loading activity
If you want to migrate from SelfServicing to Adapter in an existing project, the question arises where your code might trigger lazy loading calls. To
find these, you can override two methods in a partial class of CommonEntityBase
: OnSingleEntityLazyLoading
and OnMultiEntityLazyLoading
. These
methods are called when a single entity resp. a multi-entity lazy loading activity is started. These methods do nothing but overriding them could give
you the opportunity to trap where lazy loading activity is triggered and e.g. correct it by examining the call stack.