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 parametersAction: 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
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
Pagination: Built-in pagination with PageNumber and PageSize
Metadata: Includes total count, page count, and pagination info
Large Datasets: Optimized for handling large amounts of data
Data Grids: Perfect for admin interfaces and data tables
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)
GET Request β Controller receives request with query parameters
Service Processing β Business orchestration and query resolution
Query Handler β Database query building and execution
AutoMapper β Entity-to-DTO transformation
Pagination β Apply pagination to results
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
Database Indexing: Ensure proper indexes on filter and sort fields
Selective Fields: Only return necessary fields in DTO
Query Optimization: Use efficient WHERE clauses and sorting
Page Size Limits: Set reasonable maximum page sizes
Caching: Consider caching for frequently accessed data
When to Use Get Paged List vs 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