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.
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) |
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) |
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.
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
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 |
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".
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.