Get List Example
Overview
This document demonstrates the complete Get List flow using the GetShippingDetails 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 is similar to Get Paged List but returns a simple IEnumerable<T> without pagination.
Complete Flow Architecture
GET Request → Controller → Service → Query Handler → RESTClient → External System
↓
Response ← Controller ← Service ← Query Handler ← RESTClient ← External SystemDetailed 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 (Simple List)Step-by-Step Implementation
1. API Controller - The Entry Point
File: ShippingController_GetShippingDetails.cs
[HttpGet()]
[Route("GetShippingDetails")]
[ProducesResponseType(typeof(IEnumerable<GetShippingDetailsDto>), 200)]
public async Task<IActionResult> GetShippingDetails([FromQuery]GetShippingDetailsParams parameters)
{
return RunQueryListService<GetShippingDetailsParams, GetShippingDetailsDto>(
parameters, _processShippingService.GetShippingDetails);
}What Happens:
HTTP Method:
GET /api/Shipping/GetShippingDetailsInput:
GetShippingDetailsParamsfrom query parametersAction: Calls the service layer to process the query
Response: HTTP 200 OK with
IEnumerable<GetShippingDetailsDto>
2. Service Layer - Business Orchestration
File: ProcessShippingService_GetShippingDetails.cs
public IEnumerable<GetShippingDetailsDto> GetShippingDetails(GetShippingDetailsParams @params)
{
return _flexHost.GetFlexiQuery<GetShippingDetails>().AssignParameters(@params).Fetch();
}What Happens:
Query Resolution: Gets the FlexiQuery instance for GetShippingDetails
Parameter Assignment: Assigns query parameters to the query handler
Query Execution: Calls the Fetch() method to execute the query
Result: Returns simple list of shipping details for lookup
3. Query Handler - RESTClient Integration
File: GetShippingDetails.cs
public class GetShippingDetails : FlexiQueryEnumerableBridge<Shipping, GetShippingDetailsDto>
{
protected readonly ILogger<GetShippingDetails> _logger;
protected GetShippingDetailsParams _params;
protected readonly ShippingRESTClient _restClient;
public GetShippingDetails(ILogger<GetShippingDetails> logger, ShippingRESTClient restClient)
{
_logger = logger;
_restClient = restClient;
}
public virtual GetShippingDetails AssignParameters(GetShippingDetailsParams @params)
{
_params = @params;
return this;
}
public override IEnumerable<GetShippingDetailsDto> Fetch()
{
try
{
// Convert internal parameters to Request DTO
GetShippingDetailsRequestDto requestParams = new GetShippingDetailsRequestDto
{
SearchTerm = _params.SearchTerm,
OrderId = _params.OrderId,
Status = _params.Status?.ToString(),
FromDate = _params.FromDate,
ToDate = _params.ToDate,
Limit = _params.Limit
};
// Call external REST service
var response = _restClient.GetShippingDetails(requestParams).Result;
if (response.IsSuccessStatusCode)
{
var responseContent = response.Content.ReadAsStringAsync().Result;
var responseDto = JsonConvert.DeserializeObject<GetShippingDetailsResponseDto>(responseContent);
// Convert Response DTO to Internal DTO
var result = responseDto.ShippingDetails?.Select(x => new GetShippingDetailsDto
{
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,
IsActive = x.IsActive
}).ToList() ?? new List<GetShippingDetailsDto>();
_logger.LogDebug("Shipping details retrieved successfully. Count: {Count}", result.Count);
return result;
}
else
{
_logger.LogWarning("Failed to retrieve shipping details. Status: {StatusCode}", response.StatusCode);
return new List<GetShippingDetailsDto>();
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error retrieving shipping details");
return new List<GetShippingDetailsDto>();
}
}
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 list
Error Handling: Handles success/failure responses from external system
Data Transformation: Applies business logic during mapping
4. Query Parameters - Input DTO
File: GetShippingDetailsParams.cs
public class GetShippingDetailsParams : DtoBridge
{
// Add custom filtering parameters here
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; }
public int? Limit { get; set; }
}What Happens:
Custom Filters: Allows filtering by search term, order ID, status, date range
Limit Support: Optional limit for lookup results
Query Parameters: Automatically bound from URL query string
5. Output DTO - Data Transfer Object
File: GetShippingDetailsDto.cs
public partial class GetShippingDetailsDto : 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 bool IsActive { get; set; }
}What Happens:
Lookup Data: Contains essential fields for dropdown/lookup scenarios
Minimal Structure: Only includes necessary fields for selection
Performance: Lightweight DTO for fast loading
6. AutoMapper Configuration - Data Transformation
File: GetShippingDetailsMapperConfiguration.cs
public partial class GetShippingDetailsMapperConfiguration : FlexMapperProfile
{
public GetShippingDetailsMapperConfiguration() : base()
{
CreateMap<Shipping, GetShippingDetailsDto>()
.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
Lookup Optimization: Maps only necessary fields for lookup scenarios
Key Differences from Get Paged List
Get List vs Get Paged List Characteristics
Return Type
IEnumerable<T>
FlexiPagedList<T>
Pagination
❌ No pagination
✅ Built-in pagination
Use Case
Lookup/Dropdown
Data grids/tables
Performance
Fast for small datasets
Optimized for large datasets
Memory Usage
Lower (no pagination metadata)
Higher (includes pagination info)
Controller Method
RunQueryListService
RunQueryPagedService
Query Base
FlexiQueryEnumerableBridge
FlexiQueryPagedListBridge
Get List-Specific Features
Simple List: Returns basic
IEnumerable<T>without paginationLookup Optimized: Designed for dropdown/lookup scenarios
Lightweight: Minimal data transfer for performance
No Pagination: All results returned in single response
Fast Loading: Optimized for quick data retrieval
Common Use Cases
Dropdown Lists: Populate dropdown controls
Lookup Tables: Search and select scenarios
Reference Data: Load reference data for forms
Auto-complete: Provide suggestions for input fields
Quick Filters: Fast filtering options
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
Response → HTTP 200 OK with simple list
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);
}
// Apply limit if specified
if (_params.Limit.HasValue)
{
query = query.Take(_params.Limit.Value);
}
return query;
}Advanced Query Building
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);
}
// Sorting for consistent results
query = query.OrderByDescending(s => s.CreatedDate);
// Apply limit for performance
if (_params.Limit.HasValue)
{
query = query.Take(_params.Limit.Value);
}
else
{
// Default limit for lookup scenarios
query = query.Take(100);
}
return query;
}Performance Considerations
Optimization Strategies
Limit Results: Always apply reasonable limits for lookup scenarios
Selective Fields: Only return necessary fields in DTO
Indexing: Ensure proper database indexes on filter fields
Caching: Consider caching for frequently accessed lookup data
Lazy Loading: Use lazy loading for related entities when appropriate
When to Use Get List vs Get Paged List
Dropdown Population
✅ Yes
❌ No
Lookup Tables
✅ Yes
❌ No
Auto-complete
✅ Yes
❌ No
Data Grids
❌ No
✅ Yes
Large Datasets
❌ No
✅ Yes
Admin Interfaces
❌ No
✅ Yes
Reporting
❌ No
✅ Yes
Key Benefits
Performance: Fast loading for small datasets
Simplicity: Simple list without pagination complexity
Lookup Optimized: Perfect for dropdown/lookup scenarios
Lightweight: Minimal data transfer
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 GetShippingDetails example demonstrates how FlexBase enables clean, maintainable, and scalable list operations optimized for lookup and dropdown scenarios! 🚀
Last updated