Get Paged List Example

Overview

This document demonstrates the complete Get Paged List flow using the GetShippingPagedList feature from the EBusiness application. The flow starts with a GET request to the controller and uses Query projects instead of Handlers in the DomainHandler section for data retrieval. This returns a paginated list of entities with metadata for data grids and large datasets.

Complete Flow Architecture

GET Request β†’ Controller β†’ Service β†’ Query Handler β†’ RESTClient β†’ External System
                                                                    ↓
Response ← Controller ← Service ← Query Handler ← RESTClient ← External System

Detailed Flow Breakdown

1. GET Request
   ↓
2. Controller (API Entry Point)
   ↓
3. Service Layer (Business Orchestration)
   ↓
4. Query Handler (RESTClient Integration)
   β”œβ”€β”€ Internal DTO β†’ Request DTO Mapping
   β”œβ”€β”€ RESTClient Call to External System
   └── Response DTO β†’ Internal DTO Mapping
   ↓
5. Response (Paged List with Metadata)

Step-by-Step Implementation

1. API Controller - The Entry Point

File: ShippingController_GetShippingPagedList.cs

[HttpGet()]
[Route("GetShippingPagedList")]
[ProducesResponseType(typeof(FlexiPagedList<GetShippingPagedListDto>), 200)]
public async Task<IActionResult> GetShippingPagedList([FromQuery]GetShippingPagedListParams parameters)
{
    return RunQueryPagedService<GetShippingPagedListParams, GetShippingPagedListDto>(
                parameters, _processShippingService.GetShippingPagedList);
}

What Happens:

  • HTTP Method: GET /api/Shipping/GetShippingPagedList

  • Input: GetShippingPagedListParams from query parameters

  • Action: Calls the service layer to process the query

  • Response: HTTP 200 OK with FlexiPagedList<GetShippingPagedListDto>

2. Service Layer - Business Orchestration

File: ProcessShippingService_GetShippingPagedList.cs

public FlexiPagedList<GetShippingPagedListDto> GetShippingPagedList(GetShippingPagedListParams @params)
{
    return _flexHost.GetFlexiQuery<GetShippingPagedList>().AssignParameters(@params).Fetch();
}

What Happens:

  • Query Resolution: Gets the FlexiQuery instance for GetShippingPagedList

  • Parameter Assignment: Assigns query parameters to the query handler

  • Query Execution: Calls the Fetch() method to execute the query

  • Result: Returns paginated list of shipping with metadata

3. Query Handler - RESTClient Integration

File: GetShippingPagedList.cs

public class GetShippingPagedList : FlexiQueryPagedListBridge<Shipping, GetShippingPagedListDto>
{
    protected readonly ILogger<GetShippingPagedList> _logger;
    protected GetShippingPagedListParams _params;
    protected readonly ShippingRESTClient _restClient;

    public GetShippingPagedList(ILogger<GetShippingPagedList> logger, ShippingRESTClient restClient)
    {
        _logger = logger;
        _restClient = restClient;
    }

    public virtual GetShippingPagedList AssignParameters(GetShippingPagedListParams @params)
    {
        _params = @params;
        return this;
    }

    public override FlexiPagedList<GetShippingPagedListDto> Fetch()
    {
        try
        {
            // Convert internal parameters to Request DTO
            GetShippingPagedListRequestDto requestParams = new GetShippingPagedListRequestDto
            {
                PageNumber = _params.PageNumber,
                PageSize = _params.PageSize,
                SearchTerm = _params.SearchTerm,
                OrderId = _params.OrderId,
                Status = _params.Status?.ToString(),
                FromDate = _params.FromDate,
                ToDate = _params.ToDate,
                SortBy = _params.SortBy,
                SortDirection = _params.SortDirection
            };

            // Call external REST service
            var response = _restClient.GetShippingPagedList(requestParams).Result;

            if (response.IsSuccessStatusCode)
            {
                var responseContent = response.Content.ReadAsStringAsync().Result;
                var responseDto = JsonConvert.DeserializeObject<GetShippingPagedListResponseDto>(responseContent);
                
                // Convert Response DTO to Internal DTO
                var items = responseDto.Items?.Select(x => new GetShippingPagedListDto
                {
                    Id = x.Id,
                    OrderId = x.OrderId,
                    ShippingAddress = x.ShippingAddress,
                    TrackingNumber = x.TrackingNumber,
                    Status = ParseShippingStatus(x.Status),
                    StatusDescription = x.StatusDescription,
                    TotalWeight = x.TotalWeight,
                    CreatedDate = x.CreatedDate,
                    CreatedBy = x.CreatedBy,
                    IsActive = x.IsActive
                }).ToList() ?? new List<GetShippingPagedListDto>();

                // Create paginated result
                var result = new FlexiPagedList<GetShippingPagedListDto>
                {
                    Items = items,
                    TotalCount = responseDto.TotalCount,
                    PageNumber = responseDto.PageNumber,
                    PageSize = responseDto.PageSize,
                    TotalPages = responseDto.TotalPages,
                    HasPreviousPage = responseDto.HasPreviousPage,
                    HasNextPage = responseDto.HasNextPage
                };

                _logger.LogDebug("Shipping paged list retrieved successfully. Count: {Count}, Page: {PageNumber}", 
                    result.TotalCount, result.PageNumber);
                return result;
            }
            else
            {
                _logger.LogWarning("Failed to retrieve shipping paged list. Status: {StatusCode}", response.StatusCode);
                return new FlexiPagedList<GetShippingPagedListDto>
                {
                    Items = new List<GetShippingPagedListDto>(),
                    TotalCount = 0,
                    PageNumber = _params.PageNumber,
                    PageSize = _params.PageSize,
                    TotalPages = 0,
                    HasPreviousPage = false,
                    HasNextPage = false
                };
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error retrieving shipping paged list");
            return new FlexiPagedList<GetShippingPagedListDto>
            {
                Items = new List<GetShippingPagedListDto>(),
                TotalCount = 0,
                PageNumber = _params.PageNumber,
                PageSize = _params.PageSize,
                TotalPages = 0,
                HasPreviousPage = false,
                HasNextPage = false
            };
        }
    }

    private ShippingStatus ParseShippingStatus(string status)
    {
        return status?.ToLower() switch
        {
            "created" => ShippingStatus.Created,
            "in_transit" => ShippingStatus.InTransit,
            "delivered" => ShippingStatus.Delivered,
            "cancelled" => ShippingStatus.Cancelled,
            _ => ShippingStatus.Unknown
        };
    }
}

What Happens:

  • Parameter Assignment: Stores query parameters for use in RESTClient calls

  • Request Mapping: Converts internal parameters to Request DTO

  • RESTClient Call: Calls external shipping service via RESTClient

  • Response Mapping: Converts Response DTO to Internal DTO with pagination

  • Error Handling: Handles success/failure responses from external system

  • Data Transformation: Applies business logic during mapping

4. Query Parameters - Input DTO

File: GetShippingPagedListParams.cs

public class GetShippingPagedListParams : DtoBridge
{
    // Pagination parameters
    public int PageNumber { get; set; } = 1;
    public int PageSize { get; set; } = 10;
    
    // Filtering parameters
    public string SearchTerm { get; set; }
    public string OrderId { get; set; }
    public ShippingStatus? Status { get; set; }
    public DateTime? FromDate { get; set; }
    public DateTime? ToDate { get; set; }
    
    // Sorting parameters
    public string SortBy { get; set; } = "CreatedDate";
    public string SortDirection { get; set; } = "desc";
}

What Happens:

  • Pagination: PageNumber and PageSize for pagination control

  • Filtering: Multiple filter options for data refinement

  • Sorting: SortBy and SortDirection for data ordering

  • Query Parameters: Automatically bound from URL query string

5. Output DTO - Data Transfer Object

File: GetShippingPagedListDto.cs

public partial class GetShippingPagedListDto : DtoBridge 
{
    public string Id { get; set; }
    public string OrderId { get; set; }
    public string ShippingAddress { get; set; }
    public string TrackingNumber { get; set; }
    public ShippingStatus Status { get; set; }
    public string StatusDescription { get; set; }
    public decimal TotalWeight { get; set; }
    public DateTime CreatedDate { get; set; }
    public string CreatedBy { get; set; }
    public bool IsActive { get; set; }
}

What Happens:

  • Grid Data: Contains fields suitable for data grid display

  • Essential Fields: Includes key information for list views

  • Performance: Optimized for large dataset display

6. AutoMapper Configuration - Data Transformation

File: GetShippingPagedListMapperConfiguration.cs

public partial class GetShippingPagedListMapperConfiguration : FlexMapperProfile
{
    public GetShippingPagedListMapperConfiguration() : base()
    {
        CreateMap<Shipping, GetShippingPagedListDto>()
            .ForMember(d => d.StatusDescription, opt => opt.MapFrom(s => s.Status.ToString()))
            .ForMember(d => d.IsActive, opt => opt.MapFrom(s => !s.IsSoftDeleted));
    }
}

What Happens:

  • Entity to DTO Mapping: Maps domain entities to DTOs

  • Custom Mappings: Handles complex property transformations

  • Grid Optimization: Maps fields optimized for data grid display

Key Differences from Get List

Get Paged List vs Get List Characteristics

Aspect
Get Paged List
Get List

Return Type

FlexiPagedList<T>

IEnumerable<T>

Pagination

βœ… Built-in pagination

❌ No pagination

Use Case

Data grids/tables

Lookup/Dropdown

Performance

Optimized for large datasets

Fast for small datasets

Memory Usage

Higher (includes pagination info)

Lower (no pagination metadata)

Controller Method

RunQueryPagedService

RunQueryListService

Query Base

FlexiQueryPagedListBridge

FlexiQueryEnumerableBridge

Get Paged List-Specific Features

  1. Pagination: Built-in pagination with PageNumber and PageSize

  2. Metadata: Includes total count, page count, and pagination info

  3. Large Datasets: Optimized for handling large amounts of data

  4. Data Grids: Perfect for admin interfaces and data tables

  5. Performance: Efficient memory usage for large datasets

Common Use Cases

  • Data Grids: Populate data tables with pagination

  • Admin Interfaces: Large dataset management

  • Reporting: Paginated report data

  • Search Results: Paginated search results

  • Audit Logs: Large audit trail displays

Flow Summary

Synchronous Flow (Data Retrieval)

  1. GET Request β†’ Controller receives request with query parameters

  2. Service Processing β†’ Business orchestration and query resolution

  3. Query Handler β†’ Database query building and execution

  4. AutoMapper β†’ Entity-to-DTO transformation

  5. Pagination β†’ Apply pagination to results

  6. Response β†’ HTTP 200 OK with paginated list and metadata

No Asynchronous Flow

  • No Events: Query operations don't publish events

  • No Subscribers: No side effects or event processing

  • Immediate Response: Data is returned immediately

Query Building Patterns

Basic Query Building

protected override IQueryable<T> Build<T>()
{
    _repoFactory.Init(_params);
    IQueryable<T> query = _repoFactory.GetRepo().FindAll<T>();
    
    // Add filtering logic here
    if (!string.IsNullOrEmpty(_params.SearchTerm))
    {
        query = query.Where(s => s.ShippingAddress.Contains(_params.SearchTerm) || 
                                s.TrackingNumber.Contains(_params.SearchTerm));
    }
    
    if (!string.IsNullOrEmpty(_params.OrderId))
    {
        query = query.Where(s => s.OrderId == _params.OrderId);
    }
    
    if (_params.Status.HasValue)
    {
        query = query.Where(s => s.Status == _params.Status.Value);
    }
    
    return query;
}

Advanced Query Building with Sorting

protected override IQueryable<T> Build<T>()
{
    _repoFactory.Init(_params);
    IQueryable<T> query = _repoFactory.GetRepo().FindAll<T>();
    
    // Exclude soft deleted items
    query = query.Where(s => !s.IsSoftDeleted);
    
    // Search functionality
    if (!string.IsNullOrEmpty(_params.SearchTerm))
    {
        var searchTerm = _params.SearchTerm.ToLower();
        query = query.Where(s => s.ShippingAddress.ToLower().Contains(searchTerm) || 
                                s.TrackingNumber.ToLower().Contains(searchTerm) ||
                                s.OrderId.ToLower().Contains(searchTerm));
    }
    
    // Order ID filtering
    if (!string.IsNullOrEmpty(_params.OrderId))
    {
        query = query.Where(s => s.OrderId == _params.OrderId);
    }
    
    // Status filtering
    if (_params.Status.HasValue)
    {
        query = query.Where(s => s.Status == _params.Status.Value);
    }
    
    // Date range filtering
    if (_params.FromDate.HasValue)
    {
        query = query.Where(s => s.CreatedDate >= _params.FromDate.Value);
    }
    
    if (_params.ToDate.HasValue)
    {
        query = query.Where(s => s.CreatedDate <= _params.ToDate.Value);
    }
    
    // Dynamic sorting
    switch (_params.SortBy?.ToLower())
    {
        case "createddate":
            query = _params.SortDirection?.ToLower() == "asc" 
                ? query.OrderBy(s => s.CreatedDate)
                : query.OrderByDescending(s => s.CreatedDate);
            break;
        case "status":
            query = _params.SortDirection?.ToLower() == "asc" 
                ? query.OrderBy(s => s.Status)
                : query.OrderByDescending(s => s.Status);
            break;
        case "trackingnumber":
            query = _params.SortDirection?.ToLower() == "asc" 
                ? query.OrderBy(s => s.TrackingNumber)
                : query.OrderByDescending(s => s.TrackingNumber);
            break;
        default:
            query = query.OrderByDescending(s => s.CreatedDate);
            break;
    }
    
    return query;
}

Pagination Metadata

FlexiPagedList Structure

public class FlexiPagedList<T>
{
    public IEnumerable<T> Items { get; set; }
    public int TotalCount { get; set; }
    public int PageNumber { get; set; }
    public int PageSize { get; set; }
    public int TotalPages { get; set; }
    public bool HasPreviousPage { get; set; }
    public bool HasNextPage { get; set; }
}

Frontend Integration Example

// Example frontend usage
const response = await fetch('/api/Shipping/GetShippingPagedList?pageNumber=1&pageSize=10&searchTerm=tracking');
const pagedData = await response.json();

console.log(`Total items: ${pagedData.totalCount}`);
console.log(`Page ${pagedData.pageNumber} of ${pagedData.totalPages}`);
console.log(`Items:`, pagedData.items);

Performance Considerations

Optimization Strategies

  1. Database Indexing: Ensure proper indexes on filter and sort fields

  2. Selective Fields: Only return necessary fields in DTO

  3. Query Optimization: Use efficient WHERE clauses and sorting

  4. Page Size Limits: Set reasonable maximum page sizes

  5. Caching: Consider caching for frequently accessed data

When to Use Get Paged List vs Get List

Scenario
Use Get Paged List
Use Get List

Data Grids

βœ… Yes

❌ No

Large Datasets

βœ… Yes

❌ No

Admin Interfaces

βœ… Yes

❌ No

Reporting

βœ… Yes

❌ No

Dropdown Population

❌ No

βœ… Yes

Lookup Tables

❌ No

βœ… Yes

Auto-complete

❌ No

βœ… Yes

Error Handling

HTTP Status Codes

  • 200 OK: Data retrieved successfully

  • 400 Bad Request: Invalid parameters (invalid page number, page size)

  • 500 Internal Server Error: Database or server error

Parameter Validation

[HttpGet()]
[Route("GetShippingPagedList")]
[ProducesResponseType(typeof(FlexiPagedList<GetShippingPagedListDto>), 200)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> GetShippingPagedList([FromQuery]GetShippingPagedListParams parameters)
{
    // Validate pagination parameters
    if (parameters.PageNumber < 1)
    {
        return BadRequest("Page number must be greater than 0");
    }
    
    if (parameters.PageSize < 1 || parameters.PageSize > 100)
    {
        return BadRequest("Page size must be between 1 and 100");
    }
    
    return RunQueryPagedService<GetShippingPagedListParams, GetShippingPagedListDto>(
                parameters, _processShippingService.GetShippingPagedList);
}

Key Benefits

  • Performance: Optimized for large datasets with pagination

  • Memory Efficiency: Only loads required page of data

  • User Experience: Smooth navigation through large datasets

  • Metadata: Rich pagination information for frontend

  • Type Safety: Strongly typed DTOs and parameters

  • AutoMapper: Automatic entity-to-DTO mapping

  • No Side Effects: Read-only operations

  • Testable: Each component can be tested independently

  • Maintainable: Clear separation of concerns


This GetShippingPagedList example demonstrates how FlexBase enables clean, maintainable, and scalable paginated list operations optimized for data grids and large datasets! πŸš€

Last updated