Up Query using property navigation
DevForce 2010 Resource Center » DevForce development » Query » Query using property navigation » Navigation properties and data retrieval

Navigation properties and data retrieval

Last modified on August 15, 2012 17:21

Any time a navigation property is accessed, DevForce needs to determine whether to try to fetch the related entity or entities from the backend datastore or to use previously retrieved data. In general, the first time any navigation property on an entity is accessed, data will be retrieved from the database and all subsequent attempts to access the property will use the local cache. Navigation properties and data retrieval can be modified in several ways.


Navigation properties

Every navigation property on an entity has a corresponding EntityReferenceBase.  This EntityReferenceBase can be obtained by calling NavigationEntityProperty.GetEntityReference method.  Depending on whether the navigation property returns a scalar or a list, the actual EntityReference returned will be either a ScalarEntityReference<T> or a ListEntityReference<T>.

The EntityReference instance returned itself has an IsLoaded property that is settable. This may be set on a per entity/per navigation property basis. This property is useful to determine whether or not a navigation property has been resolved, but it can also be used to stop a property navigation from occuring or to force a refresh of a navigation property. For example to force the next access of myCustomer.Orders to go to the database the following code would be used.

C#
Customer.PropertyMetadata.Orders.GetEntityReference(myCustomer).IsLoaded = false;
VB
Customer.PropertyMetadata.Orders.GetEntityReference(myCustomer).IsLoaded = False

Every navigation property has its default 'loading' behavior determined by a ReferenceStrategy that may be set either 

1) at the EntityManager level via the DefaultEntityReferenceStrategy.  The example below shows how to force every access of any navigation property to go to the database and to have the results of that navigation overwrite any local changes.

C#
myEntityManager.DefaultEntityReferenceStrategy =
 new EntityReferenceStrategy(EntityReferenceLoadStrategy.Load,
    MergeStrategy.OverwriteChanges);
VB
MyEntityManager.DefaultEntityReferenceStrategy = _
 New EntityReferenceStrategy(EntityReferenceLoadStrategy.Load, _
    MergeStrategy.OverwriteChanges)

 
  or

2) on a specific EntityProperty. Note that setting a reference strategy at the property level is a global setting for this property across all entities and EntityManagers. This setting overrides the EntityManager's DefaultEntityReferenceStrategy for this property. For example, to force every access of any Customer's Orders property to go to the database and to have the results of this navigation overwrite any local changes the following code would be used. 

C#
Customer.PropertyMetadata.Orders.ReferenceStrategy =
 new EntityReferenceStrategy(EntityReferenceLoadStrategy.Load,
    MergeStrategy.OverwriteChanges);
VB
Customer.PropertyMetadata.Orders.ReferenceStrategy = _
 New EntityReferenceStrategy(EntityReferenceLoadStrategy.Load, _
    MergeStrategy.OverwriteChanges)

Components of an EntityReferenceStrategy

As shown in the examples above, a ReferenceStrategy is itself is made up of a EntityReferenceLoadStrategy and a MergeStrategy.   The EntityReferenceLoadStrategy determines under what conditions data should be 'loaded' ( i.e. retrieved from the database) and the MergeStrategy determines how that data, if loaded, gets merged into the local entity cache. 

LoadStrategy  Meaning
Lazy  first access to the property should go to the database; subsequent accesses should use the local cache
Load  for every access always try to load from the database
DoNotLoad  never try to load from the database; property access always looks in cache. You can load the entities manually as described below.

The default LoadStrategy is Lazy; the default MergeStrategy is PreserveChanges. These concepts are also discussed under the Entity cache and Entity metadata topics.

Asynchronous navigations

There is actually a third part of every EntityReferenceStrategy that determines whether the related property navigation should be performed asynchronously or not. This is represented by the IsAsync property on an EntityReference as is usually defaulted based on the runtime environment; in Silverlight IsAsync=true and in the Desktop CLR IsAsync=false. However, there is a seperate constructor overload that will allow you to set the IsAsync flag to something other than the default. It's signature is the same as that shown above with one additional final boolean parameter

Resetting the default EntityReferenceStrategy

As mentioned earlier, DevForce provides a way of specifying a 'default' EntityReferenceStrategy via the DefaultEntityReferenceStrategy. This is useful because it is very common for applications to have a 'preferred' model for property navigation.  Every EntityReference associated with an entity within an EntityManager will use this EntityReferenceStrategy by default. Specifying an EntityReferenceStrategy for a navigation property overrides this default, as was shown above.  Resetting a navigation property to use the default is accomplished by setting NavigationProperty's ReferenceStrategy to null. 

C#
Customer.PropertyMetadata.Orders.ReferenceStrategy = null;
VB
Customer.PropertyMetadata.Orders.ReferenceStrategy = Nothing

Loaded state

The ReferenceStrategy governs the behavior of a navigation property for all instances of the entity type. You can tweak the current state of a particular entity's navigation property. For example, you can force the "cust" Customer entity to act as if its orders have already been loaded into cache:

C#
Customer.PropertyMetadata.Orders
        .GetEntityReference(cust)
        .IsLoaded = true;
VB
Customer.PropertyMetadata.Orders _
        .GetEntityReference(cust) _
        .IsLoaded = True

If you didn't know the entity type or property name at compile time, you could write a general function to do this. The pertinent statement would be something like:

C#
anEntity.EntityAspect.EntityMetadata    
        .NavigationProperties       
        .First(p => p.Name == somePropertyName)
        .GetEntityReference(anEntity)
        .IsLoaded = true;
VB
anEntity.EntityAspect.EntityMetadata _
        .NavigationProperties _
        .First(Function(p) p.Name = somePropertyName) _
        .GetEntityReference(anEntity) _
        .IsLoaded = True

This statement would have the same effect as before if anEntity == cust and somePropertyName == "Orders".

Force loading

If you set an entity type's LoadStrategy to DoNotLoad as we did above, you'll have to manually manage the loading of the property's data. Sometimes you want to force a navigation property to refresh. Here's one way to force a (re)load of the example customer's orders:

C#
Customer.PropertyMetadata.Orders
        .GetEntityReference(cust)
        .Load(MergeStrategy.PreserveChanges);
VB
Customer.PropertyMetadata.Orders _
        .GetEntityReference(cust) _
        .Load(MergeStrategy.PreserveChanges)

The navigation property load operation runs asynchronously in Silverlight, synchronously elsewhere.

It may be easier simply to query cust's orders directly from the database after which cust.Orders will find them in cache.

Tags: Navigation
Created by DevForce on December 06, 2010 16:34

This wiki is licensed under a Creative Commons 2.0 license. XWiki Enterprise 3.2 - Documentation. Copyright © 2012 IdeaBlade