Skip to main content

Querying Data

  • 6 minutes to read

Querying (or reading) data is one of the CRUD operations available in all data stores. By querying data with the ExpressEntityMapping Framework, you retrieve data store records as entity objects only if they match specific criteria. You can use LINQ expressions, object-based criteria, and strings that follow a database-independent criteria syntax to build queries. The sections below provide details on these options.

Object-Based Criteria

This method allows you to retrieve entity objects that match specific criteria by:

  • Calling a session component’s GetObjects or Find function and passing the criteria expression and entity class type as parameters;

  • Assigning a TdxEMFDataSet component’s Criteria property.

You can build criteria expressions using operator and operand classes declared in the dxEMF.DB.Criteria unit. Each operator accepts one or more operands, which can be constants, values, entity properties, or other operators. With this flexible expression model, you can build complex criteria by combining simple criteria expressions.

The following table lists all available operator and operand classes by category.

Category

Class

Description

Operator

TdxAggregateOperand

An aggregate operator that calculates aggregate expressions (SUM, MIN, MAX, etc.)

TdxBetweenOperator

An operator that checks if an operand value falls within a specified range.

TdxBinaryOperator

An operator that evaluates a TdxBinaryOperatorType operation with two operands.

TdxContainsOperator

An operator that checks if a collection contains at least one object matching specific criteria.

TdxFunctionOperator

An operator based on a TdxFunctionOperatorType function.

TdxGroupOperator

An operator that evaluates a logical AND or OR operation with two or more operands.

TdxInOperator

An operator that checks if an operand value matches any element in a specified list.

TdxJoinOperand

An operator that joins entity objects on a specified condition, and calculates aggregate functions against matching objects.

TdxUnaryOperator

An operator that evaluates an unary operation (with only one operand).

Operand

TdxConstantValue

A constant value operand.

TdxOperandProperty

An object property operand.

TdxOperandValue

A value operand.

The code example below uses a TdxBinaryOperator object and a generic GetObjects function to retrieve all persons under the age of 30.

uses
  ..., dxEMF.Core, dxEMF.Types, dxEMF.DB.Criteria;
implementation
// ...
var
  ACollection: IdxEMFCollection<TPerson>;
  ACriteriaOperator: IdxCriteriaOperator;
//...
begin
//...
  ACriteriaOperator := TdxBinaryOperator.Create('Age', 30, TdxBinaryOperatorType.Less);
  ACollection := dxEMFSession.GetObjects<TPerson>(ACriteriaOperator);
end;

Query results are returned as object collections, which you can enumerate and access elements (entity objects) and their data. The following code enumerates the resulting object collection and obtains names of persons contained in it as a semicolon-delimited list.

var
  APerson: TPerson;
  APersonNames: string;
//...
begin
//...
  for APerson in ACollection do
    APersonNames := APersonNames + APerson.Name + ';';
end;

Database-Independent Criteria Strings

Unlike the previous method, you define a criteria expression as a string using a database-independent language and parse this string into criteria operands and operators by calling the TdxCriteriaOperator.Parse or TdxCriteriaOperator.ParseList class function. Then, you can pass the resulting criteria expression as a parameter to a session component’s GetObjects or Find function, as described above. The TdxEMFDataSet component allows you to specify filter and group criteria as strings via the CriteriaText and GroupCriteriaText properties.

The following code example calls the TdxCriteriaOperator.Parse function to create a criteria expression that is identical to the one described in the previous topic section.

ACriteriaOperator := TdxCriteriaOperator.Parse('Age < 30');

You can also pass operands as parameters and use question marks in a string as operand placeholders. The following criteria expressions are identical.

ACriteriaOperator1 := TdxCriteriaOperator.Parse('Age < 30 and YearOf(HireDate) >= 2010');
  ACriteriaOperator2 := TdxCriteriaOperator.Parse('Age < ? and YearOf(HireDate) >= ?', [30, 2010]);

LINQ Expressions

Language Integrated Query (LINQ) introduces query capabilities to programming languages by adding a set of methods that allow writing SQL-like query expressions based on types and their members. In addition to improving code readability, LINQ expressions help validate column names used in queries at design time. Refer to the “Language Integrated Query” online article at https://en.wikipedia.org/wiki/Language_Integrated_Query to learn more.

Our LINQ implementation (currently supported in Delphi only) extends query capabilities with support for entities. It is based on overloaded operators and interfaces whose function declarations replicate an entity model, its classes, and their properties. You do not need to implement these interfaces – they are building blocks for LINQ expressions and contracts for parameters used in expression clauses. In addition, you can store query and query expression objects in variables declared as interface types to automatically destroy these objects.

In the ExpressEntityMapping Framework, a LINQ query is a sequence of function calls made from another call’s result. Functions are equivalent to LINQ clauses and bear the same names.

Each LINQ query includes:

  • A Linq function call, which starts a query expression;

  • A From function call specifying a data context and a queried entity. A data context is the source of all queryable entities in your entity model. A data context references a session component to load data for entity objects returned as the query result;

  • Optional Where and/or OrderBy function calls specifying the filter criteria and how entity objects are sorted in the query result;

  • A Select function call, which finishes the query expression’s main part;

  • Optional First, Take, and/or Skip function calls modifying the filter criteria and narrowing the query result.

The following code is an example of a LINQ query expression created for the TPerson entity class described in the Entity Model Design and Customization topic.

AQuery := Linq.
    From<TPerson>(APersonExpression).
    Where(APersonExpression.Age < 30).
    OrderBy(APersonExpression.Name).
    Select.
    Take(5);

To enable the use of LINQ expressions, add the dxEMF.Linq and dxEMF.Linq.Expressions units shipped with the ExpressEntityMapping Framework to your project. Then, add declarations of interfaces that correspond to your entity model and register them for entity-based LINQ expressions by adding TdxLinqExpressionFactory.Register method calls to the ‘initialization’ section. We recommend that you use the Entity Model Designer to generate a unit file that contains all the interfaces and initialization code sufficient for creating LINQ expressions based on your project’s entity model.

The interfaces and the initialization code required for the TPerson entity class can be declared as follows:

uses
  ..., dxEMF.Linq, dxEMF.Linq.Expressions;
//...
type
  IPersonExpression = interface(IdxEntityInfo)
  ['{DF35C49D-1E2C-4207-BF54-AEE64F713120}']
    function Id: TdxLinqExpression;
    function Age: TdxLinqExpression;
    function Name: TdxLinqExpression;
  end;
  IDataModelContext = interface(IdxDataContext)
  ['{3D1FE13A-33C7-411A-8EE2-BC9CF55CD619}']
    function Person: IPersonExpression;
  end;
//...
implementation
//...
initialization
  TdxLinqExpressionFactory.Register<TPerson, IPersonExpression>;

The following code example creates a LINQ query that is identical to the criteria described in the previous topic sections.

uses
  ..., dxEMF.Core, dxEMF.Types, dxEMF.DB.Criteria, dxEMF.Linq, dxEMF.Linq.Expressions;
//...
var
  ADataContext: IDataModelContext;
  APersonExpression: IPersonExpression;
  AQuery: IdxQueryable<TPerson>;
//...
begin
//...
  // Obtaining a LINQ data context (it's the source of all queryable entities in your entity model)
  ADataContext := dxEMFSession.GetDataContext<IDataModelContext>;
  // Creating a LINQ expression referencing a Person entity from which a query retrieves data
  APersonExpression := ADataContext.Person;

The following line is equivalent to the two above.

APersonExpression := dxEMFSession.GetEntityInfo<IPersonExpression>;

A LINQ query can be created using an expression tree, as shown below.

AQuery := Linq.
    From<TPerson>(APersonExpression).
    Where(APersonExpression.Age < 30).
    Select;

Note that any LINQ query inherits either from IEnumerable or IEnumerable<T>. A LINQ query retrieves entity objects only when the enumeration executes it. In addition, since variables are declared as interface types, you don’t need to manually destroy them.

The following code enumerates the query and obtains names of persons retrieved by the query’s enumeration as a semicolon-delimited list.

for APerson in AQuery do
    APersonNames := APersonNames + APerson.Name + ';';
end;