Flow of Execution
This article is relevant to entity models that utilize the deprecated Visual Studio integration of Telerik Data Access. The current documentation of the Data Access framework is available here.
Queries against Telerik Data Access are represented by command tree queries, which execute against the OpenAccessContext. The following is the process for creating and executing a LINQ query:
- Compose a LINQ query in C# or Visual Basic by using the OpenAccessContext instance.
- Convert LINQ standard query operators and expressions to command trees.
- Execute the query against the data source. Any exceptions thrown on the data source during execution are passed directly up to the client.
- Return query results back to the client.
Composing the Query
A query is typically constructed from an existing OpenAccessContext, instead of being manually constructed, and always belongs to that OpenAccessContext. This context provides the connection and metadata information that is required to compose and execute the query. The generic IQueryable interface, whose builder methods enable LINQ queries to be incrementally built, is the center piece. In a query, you exactly specify the information that you want to retreive from the data source. A query can also specify how that information should be sorted, grouped, and shaped before it is returned. In LINQ, a query is stored in a variable. This query variable takes no action and returns no data; it only stores the query information. After you create a query, you must execute that query to retrieve any data.
The following example in query expression syntax constructs a query and uses Select to return all the rows from Products table.
IQueryable<Product> query = from product in dbContext.Products
select product;
Dim query As IQueryable(Of Product) = From product In dbContext.Products
Select product
The following code snippet achieves the same result. However, it uses a method-bassed query syntax.
using ( EntitiesModel dbContext = new EntitiesModel() )
{
IQueryable<Product> query = dbContext.Products.Select( p => p );
}
Using dbContext As New EntitiesModel()
Dim query As IQueryable(Of Product) = dbContext.Products.Select(Function(p) p)
End Using
Query Conversion
To execute a LINQ query against OpenAccessContext, the LINQ query must be converted to a command tree representation that can be executed.
LINQ queries are comprised of LINQ standard query operators (such as Select, Where, GroupBy and OrderBy) and expressions (x<10, Customer.CustomerID, and so on). LINQ operators are not defined by a class, but are rather methods on a class. In LINQ, expressions can contain anything allowed by types, by extension, anything that can be represented in a lambda function. This is a superset of the expressions, which are by definition restricted to operations allowed on the database, and supported by the OpenAccessContext.
In Telerik Data Access, both operators and expressions are represented by a single type hierarchy, which are then placed in a command tree. The command tree is used by the OpenAccessContext to execute the query. If the LINQ query cannot be expressed as a command tree, an exception will be thrown when the query is being converted. The conversion of LINQ query involves two sub-conversions: the conversion of the standard query operators, and the conversion of the expressions.
There are a number of LINQ standard query operators that do not have a valid translation. They are described later in the LINQ Support section.
Query Execution
After the LINQ query is created by the user, it is converted to a representation that is compatible with Telerik Data Access, which is then executed against the data source by the Telerik Data Access engine. At query execution time, all query expressions (or components of the query) are evaluated on the client or on the server. This includes expressions that are used in result materialization or projections.
The time in which a query expression is executed can vary. LINQ queries are executed each time the query variable is iterated over, not when the query variable is created; this is referred to as deferred execution. The query can also be forced to execute immediately, which is useful for caching query results. The following example uses Select to return all the rows from Products and display the product names. Iterating over the query variable in the foreach loop causes the query to execute.
using ( EntitiesModel dbContext = new EntitiesModel() )
{
IQueryable<string> productNames = from product in dbContext.Products
select product.ProductName;
Console.WriteLine( "Product Names:" );
foreach ( string productName in productNames )
{
Console.WriteLine( productName );
}
}
Using dbContext As New EntitiesModel()
Dim productNames As IQueryable(Of String) = From product In dbContext.Products
Select product.ProductName
Console.WriteLine("Product Names:")
For Each productName As String In productNames
Console.WriteLine(productName)
Next productName
End Using
When a LINQ query is executed, some expressions in the query might be executed on the server and some parts might be executed locally on the client. Client-side evaluation of an expression takes place before the query is executed on the server. If an expression is evaluated on the client, the result of that evaluation is substituted for the expression in the query, and the query is then executed on the server. Because queries are executed on the data source, the data source configuration overrides the behavior specified in the client. Null value handling and numerical precision are examples of this. Any exceptions thrown during query execution on the server are passed directly up to the client.
Materialization
Materialization is the process of returning query results back to the client as CLR types. The returned type could be any existing CLR type, defined by the user (or Telerik Data Access), or generated by the compiler (anonymous types).