QueryStrategy is a class in the IdeaBlade.EntityModel namespace. Every query has a QueryStrategy property that returns a instance of the QueryStrategy type. This property has a default value of null, but can be set on any query as follows:
C# | EntityQuery<Order> query01 = myEntityManager.Orders; query01.QueryStrategy = QueryStrategy.DataSourceThenCache; |
VB | Dim query01 As EntityQuery(Of Order) = myEntityManager.Orders query01.QueryStrategy = QueryStrategy.DataSourceThenCache |
In addition, every EntityManager has a DefaultQueryStrategy property that is used whenever you do not explicitly specify the query strategy you want to use with a particular query. The DefaultQueryStrategy is also used whenever you explicitly set a query's QueryStrategy property to null (Nothing in VB). By default the DefaultQueryStrategy has a value of QueryStrategy.Normal but you can also set it as follows:
C# | myEntityManager.DefaultQueryStrategy = QueryStrategy.DataSourceOnly; |
VB | myEntityManager.DefaultQueryStrategy = QueryStrategy.DataSourceOnly |
Entity navigation (e.g., myEmployee.Orders) is implemented with relation queries governed by the DefaultQueryStrategy. In addition, any query whose QueryStrategy property has a value of null will be executed with the DefaultQueryStrategy for the EntityManager under which it is run.
The QueryStrategy class is immutable and has four properties that uniquely define it:
Every QueryStrategy combines a FetchStrategy, a MergeStrategy, and a InversionMode. Since there are five FetchStrategies, five MergeStrategies, and four InversionModes, there are potentially 100 versions of QueryStrategy, even keeping the TransactionSettings constant. However, in practice, a much smaller set of QueryStrategies suffices for the great majority of purposes. DevForce has identified five of them as being of particular significance, enshrining them as static (Shared in VB) properties of the QueryStrategy class. These pre-defined QueryStrategies combine FetchStrategy, MergeStrategy, and InversionMode strategies as shown in the table below:
Fetch and merge strategies of the common query strategies
QueryStrategy | Fetch Strategy | Merge Strategy | InversionMode |
---|---|---|---|
Normal | Optimized | PreserveChanges | Try |
CacheOnly | CacheOnly | (Not Applicable) | (Not Applicable) |
DataSourceOnly | DataSourceOnly | OverwriteChanges | Off |
DataSourceOnlyWithInversion | DataSourceOnly | OverwriteChanges | On |
DataSourceThenCache | DataSourceThenCache | OverwriteChanges | Try |
Here’s how you assign a pre-defined QueryStrategy:
C# | query.QueryStrategy = QueryStrategy.DataSourceThenCache; |
VB | query.QueryStrategy = QueryStrategy.DataSourceThenCache |
As just noted, only five of the possible combinations of a FetchStrategy and a MergeStrategy are covered by the named QueryStrategies. What if you want one of the other combinations?
You can create your own QueryStrategy by supplying the fetch and merge strategy enumerations to its constructor. The result is a new immutable QueryStrategy instance. Immutable meaning that we can get the component fetch and merge strategies but we cannot reset them.
Here’s an example of the creation and assignment of a custom QueryStrategy:
C# | QueryStrategy aQueryStrategy = new QueryStrategy(FetchStrategy.DataSourceThenCache, MergeStrategy.PreserveChanges, QueryInversionMode.On); |
VB | Dim aQueryStrategy As New QueryStrategy( _ FetchStrategy.DataSourceThenCache, _ MergeStrategy.PreserveChanges, _ QueryInversionMode.On) |
There is another, often more useful, method of creating a custom QueryStrategy, via the use of one of the With overloads provided by the QueryStrategy class. These With methods allow you to create a new QueryStrategy based on an existing QueryStrategy with one of the properties changed. For example:
C# | QueryStrategy queryStrategy1 = QueryStrategy.Normal.With(FetchStrategy.DataSourceOnly); QueryStrategy queryStrategy2 = QueryStrategy.DataSourceOnly.With(MergeStrategy.PreserveChangesUpdateOriginal); QueryStrategy queryStrategy3 = queryStrategy1.With(QueryInversionMode.Off); |
VB | Dim queryStrategy1 As QueryStrategy = _ QueryStrategy.Normal.With(FetchStrategy.DataSourceOnly) Dim queryStrategy2 As QueryStrategy = _ QueryStrategy.DataSourceOnly.With(MergeStrategy.PreserveChangesUpdateOriginal) Dim queryStrategy3 As QueryStrategy = _ queryStrategy1.With(QueryInversionMode.Off) |
We mentioned earlier that the DevForce EntityManager has a DefaultQueryStrategy property that can be used to shape the fetch and merge behavior of queries where the QueryStrategy is not explicitly specified. The default setting for the EntityManager’s DefaultQueryStrategy is QueryStrategy.Normal. If you leave this setting at its default value, and in an individual query do nothing to countermand the default settings, then the FetchStrategy of Optimized will be used in combination with the MergeStrategy of PreserveChanges.
If for some reason you wanted a EntityManager where the default QueryStrategy would always involve a trip to the data source, you could assign a different QueryStrategy, such as DataSourceOnly, to the PM’s DefaultQueryStrategy property. For a given query, you could still use any desired QueryStrategy by explicitly specifying a different one.
For most users, most of the time, the DevForce defaults are perfect:
Your choice of a non-default strategy can be driven by a variety of things. For example, suppose your application supports online concert ticket sales. Your sales clerks need absolutely up-to-date information about what seats are available at the time they make a sale. In that use case, it will be essential to direct your query for available seats against the data source, so a FetchStrategy of DataSourceOnly might be in order.
In code to handle concurrency conflicts, one might need a QueryStrategy with a MergeStrategy of PreserveChangesUpdateOriginal to make an entity in conflict savable. (The data source version of the conflicted entity would only be retrieved and used to partially overwrite the cache version after the concurrency conflict had been resolved by some predetermined strategy.)
You can and will think of your own reasons to use different combinations of FetchStrategy, MergeStrategy, and InversionMode. Just ask yourself, for a given data retrieval operation, whether the data in the cache is good enough, or you need absolutely current data from the data source. Then ask yourself how you want to resolve conflicts between data already cached and duplicate incoming data. Then consider the process DevForce will use to satisfy the query and make sure it will have the data it needs to give you a correct result. DevForce gives you the flexibility to set the behavior exactly as need it.
You may find yourself with an existing IEntityQuery object that you don’t want to disturb in any way, but which you would like to run with a different QueryStrategy for a specific, one-time purpose. DevForce provides an extension method on IEntityQuery, called With(), that permits you to this. ( Our topic here is QueryStrategy, but in fact some overloads of the With() method also (or alternatively) permit you to make a one-time change to the EntityManager against which the query will be run.) Note that this is a different method from the QueryStrategy.With() method mentioned earlier.
When a call to With() is chained to a query, the result may be either a new query or a reference to the original query. Normally it will be a new query, but if the content of the With() call is such that the resultant query would be the same as the original one, a reference to the original query is returned instead of a new query.
If you ever want to be sure that you get a new query, use the Clone() extension method instead of With(). With() avoids the overhead of a Clone() when a copy is unnecessary.
C# | IEntityQuery<Customer> query00 = _em1.Customers .Where(c => c.CompanyName.ToLower().StartsWith("a")); query00.QueryStrategy = QueryStrategy.DataSourceOnly; // The With() call in the right-hand side of the following statement // specifies a query that is materially different from query0, in // that it has a different QueryStrategy associated with it. // Accordingly, the right-hand side of the statement will return // a new query: IEntityQuery<Customer> query01 = query00.With(QueryStrategy.CacheOnly); // Because the content of the With() call in the right-hand side // of the following statement doesn't result in a modification // of query0, the right-hand side will return a reference to // query0 rather than a new query. IEntityQuery<Customer> query02 = query00.With(QueryStrategy.DataSourceOnly); // If you want to be certain you get a new query, use Clone() // rather than With(): EntityQuery<Customer> query03 = (EntityQuery<Customer>)query00.Clone(); query03.QueryStrategy = QueryStrategy.DataSourceOnly; |
VB | Dim query00 As IEntityQuery(Of Customer) = _ _em1.Customers.Where(Function(c) c.CompanyName.ToLower().StartsWith("a")) query00.QueryStrategy = QueryStrategy.DataSourceOnly ' The With() call in the right-hand side of the following statement ' specifies a query that is materially different from query0, in ' that it has a different QueryStrategy associated with it. ' Accordingly, the right-hand side of the statement will return ' a new query: Dim query01 As IEntityQuery(Of Customer) = _ query00.With(QueryStrategy.CacheOnly) ' Because the content of the With() call in the right-hand side ' of the following statement doesn't result in a modification ' of query0, the right-hand side will return a reference to ' query0 rather than a new query. Dim query02 As IEntityQuery(Of Customer) = _ query00.With(QueryStrategy.DataSourceOnly) ' If you want to be certain you get a new query, use Clone() ' rather than With(): Dim query03 As EntityQuery(Of Customer) = _ CType(query00.Clone(), EntityQuery(Of Customer)) query03.QueryStrategy = QueryStrategy.DataSourceOnly |