Skip to content

iii. Query Class

Code Ninja edited this page Sep 30, 2024 · 4 revisions

What is a Query

The purpose of a query class is to execute with supported QueryEngine to fetch data from data storage.

  • QueryEngine is an implementation of IQueryEngine to execute queries against a supported data storage to return a collection of query results (ie. of type IQueryResult).

As explained above, You can configure a query in Parent or Child (nested) mode in nested hierarchies.

i. Parent Query

To define a parent or root query which is usually configured at level 1 to query the root entity, derive from aseRootQuery<TQueryParameter, TQueryResult>

  • TQueryParameter is basically the class that holds the inputs required by the root query for execution. It is an implementation of IQueryParameter type.
  • TQueryResult is the result that will be returned from executing the root query. It is an implementation of IQueryResult type.

The query parameter needs to be resolved before executing the query with QueryEngine.

In parent mode, the query parameter is resolved using the IDataContext parameter passed to data provider class.

See example CustomerQuery implemented to be configured and run in parent mode below.

internal class CustomerQuery : BaseRootQuery<CustomerParameter, CustomerResult>
   {
       public override void ResolveRootQueryParameter(IDataContext context)
       {
           // Executes as Parent or Level 1 query.
           // The query parameter is resolved using IDataContext parameter of data provider class.

           var customer = (CustomerContext)context;
           QueryParameter = new CustomerParameter
           {
               CustomerId = customer.CustomerId
           };
       }
   }

ii. Child Query

To define a child or dependant query which is usually configured as child at level below the root query to query, derive from BaseChildQuery<TQueryParameter, TQueryResult>

  • TQueryParameter is basically the class that holds the inputs required by the child query for execution. It is an implementation of IQueryParameter type.
  • TQueryResult is the result that will be returned by executing the child query. It is an implementation of IQueryResult type.

Similar to Root query, the query parameter of child query needs to be resolved before executing with QueryEngine.

In child mode, the query parameter is resolved using the query result of the parent query. You can have a maximum of 5 levels of query nestings.

See example CustomerCommunicationQuery implemented to be configured and run as child or nested query to customer query below. Please see CustomerSchema definition above for parent/child configuration setup.

   internal class CustomerCommunicationQuery : BaseChildQuery<CustomerParameter, CommunicationResult>
   {
       public override void ResolveChildQueryParameter(IDataContext context, IQueryResult parentQueryResult)
       {
           // Execute as child to customer query.
           // The result from parent customer query is used to resolve the query parameter of the nested communication query.

           var customer = (CustomerResult)parentQueryResult;
           QueryParameter = new CustomerParameter
           {
               CustomerId = customer.Id
           };
       }
   }

Supported Query Engines

Please Note: The above query implementation examples are with respect to parent/child configuration. The actual storage specific query definition should vary with specific implementation of the QueryEngine.

Please see supported Query engine implementations below.

  • Schemio.SQL - provides the implementation of IQueryEngine to execute SQL queries. Uses Dapper for SQL data acess.
  • Schemio.EntityFramework - provides implementation of IQueryEngine to execute Entity Framework queries.

i. Schemio.SQL

The SQL query needs to implement BaseSQLRootQuery<TQueryParameter, TQueryResult> or BaseSQLChildQuery<TQueryParameter, TQueryResult> based on parent or child implementation. And, requires implementing public abstract CommandDefinition GetCommandDefinition() method to return command definition for query to be executed with Dapper supported QueryEngine.

See below example CustomerQuery implemented as Root SQL query

internal class CustomerQuery : BaseSQLRootQuery<CustomerParameter, CustomerResult>
   {
       public override void ResolveRootQueryParameter(IDataContext context)
       {
           // Executes as root or level 1 query.
           var customer = (CustomerContext)context.Entity;
           QueryParameter = new CustomerParameter
           {
               CustomerId = (int)customer.CustomerId
           };
       }

      public override IEnumerable<CustomerResult> Execute(IDbConnection conn)
       {
           return conn.Query<CustomerResult>(new CommandDefinition
           (
               "select CustomerId as Id, " +
                      "Customer_Name as Name," +
                      "Customer_Code as Code " +
               $"from TCustomer where customerId={QueryParameter.CustomerId}"
          ));
       }
   }

See below example CustomerOrderItemsQuery implemented as child SQL query.

internal class CustomerOrderItemsQuery : BaseSQLChildQuery<OrderItemParameter, OrderItemResult>
   {
       public override void ResolveChildQueryParameter(IDataContext context, IQueryResult parentQueryResult)
       {
           // Execute as child query to order query taking OrderResult to resolve query parameter.
           var ordersResult = (OrderResult)parentQueryResult;

           QueryParameter ??= new OrderItemParameter();
           QueryParameter.OrderIds.Add(ordersResult.OrderId);
       }

       public override IEnumerable<OrderItemResult> Execute(IDbConnection conn)
       {
           return conn.Query<OrderItemResult>(new CommandDefinition
           (
               "select OrderId, " +
                      "OrderItemId as ItemId, " +
                      "Name, " +
                      "Cost " +
               $"from TOrderItem where OrderId in ({QueryParameter.ToCsv()})"
          ));
       }
   }

ii. Schemio.EntityFramework

The SQL query needs to implement BaseSQLRootQuery<TQueryParameter, TQueryResult> or BaseSQLChildQuery<TQueryParameter, TQueryResult> based on parent or child implementation. And, requires implementing public abstract IEnumerable<IQueryResult> Run(DbContext dbContext) method to implement query using DbContext using entity framework.

See below example CustomerQuery implemented as Root Entity framework query

internal class CustomerQuery : BaseSQLRootQuery<CustomerParameter, CustomerResult>
   {
       public override void ResolveRootQueryParameter(IDataContext context)
       {
           // Executes as root or level 1 query.
           var customer = (CustomerContext)context.Entity;
           QueryParameter = new CustomerParameter
           {
               CustomerId = (int)customer.CustomerId
           };
       }

       public override IEnumerable<IQueryResult> Run(DbContext dbContext)
       {
           return dbContext.Set<Customer>()
                       .Where(c => c.Id == QueryParameter.CustomerId)
                       .Select(c => new CustomerResult
                       {
                           Id = c.Id,
                           Name = c.Name,
                           Code = c.Code
                       });
       }
   }

See below example CustomerOrderItemsQuery implemented as child Entity framework query.

internal class CustomerOrderItemsQuery : BaseSQLChildQuery<OrderItemParameter, OrderItemResult>
   {
       public override void ResolveChildQueryParameter(IDataContext context, IQueryResult parentQueryResult)
       {
           // Execute as child query to order query taking OrderResult to resolve query parameter.
           var ordersResult = (CustomerOrderResult)parentQueryResult;

           QueryParameter ??= new OrderItemParameter();
           QueryParameter.OrderIds.Add(ordersResult.OrderId);
       }

       public override IEnumerable<IQueryResult> Run(DbContext dbContext)
       {
           return dbContext.Set<OrderItem>()
               .Where(p => QueryParameter.OrderIds.Contains(p.Order.OrderId))
               .Select(c => new OrderItemResult
               {
                   ItemId = c.ItemId,
                   Name = c.Name,
                   Cost = c.Cost,
                   OrderId = c.Order.OrderId
               });
           ;
       }
   }