Get By Id Example
Overview
This document demonstrates the complete Get By ID flow using the GetShippingInfoById 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 single entity by its unique identifier.
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 (Single Entity)
Step-by-Step Implementation
1. API Controller - The Entry Point
File: ShippingController_GetShippingInfoById.cs
[HttpGet()]
[Route("GetShippingInfoById/{id}")]
[ProducesResponseType(typeof(GetShippingInfoByIdDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetShippingInfoById(string id)
{
GetShippingInfoByIdParams parameters = new GetShippingInfoByIdParams();
parameters.Id = id;
return RunQuerySingleService<GetShippingInfoByIdParams, GetShippingInfoByIdDto>(
parameters, _processShippingService.GetShippingInfoById);
}
What Happens:
HTTP Method:
GET /api/Shipping/GetShippingInfoById/{id}
Input: Shipping ID from URL parameter
Parameter Creation: Creates
GetShippingInfoByIdParams
with the IDAction: Calls the service layer to process the query
Response: HTTP 200 OK with
GetShippingInfoByIdDto
or 404 Not Found
2. Service Layer - Business Orchestration
File: ProcessShippingService_GetShippingInfoById.cs
public GetShippingInfoByIdDto GetShippingInfoById(GetShippingInfoByIdParams @params)
{
return _flexHost.GetFlexiQuery<GetShippingInfoById>().AssignParameters(@params).Fetch();
}
What Happens:
Query Resolution: Gets the FlexiQuery instance for GetShippingInfoById
Parameter Assignment: Assigns query parameters to the query handler
Query Execution: Calls the Fetch() method to execute the query
Result: Returns single shipping or null if not found
3. Query Handler - RESTClient Integration
File: GetShippingInfoById.cs
public class GetShippingInfoById : FlexiQueryBridge<Shipping, GetShippingInfoByIdDto>
{
protected readonly ILogger<GetShippingInfoById> _logger;
protected GetShippingInfoByIdParams _params;
protected readonly ShippingRESTClient _restClient;
public GetShippingInfoById(ILogger<GetShippingInfoById> logger, ShippingRESTClient restClient)
{
_logger = logger;
_restClient = restClient;
}
public virtual GetShippingInfoById AssignParameters(GetShippingInfoByIdParams @params)
{
_params = @params;
return this;
}
public override GetShippingInfoByIdDto Fetch()
{
try
{
// Convert internal parameters to Request DTO
GetShippingInfoByIdRequestDto requestParams = new GetShippingInfoByIdRequestDto
{
Id = _params.Id
};
// Call external REST service
var response = _restClient.GetShippingInfoById(requestParams).Result;
if (response.IsSuccessStatusCode)
{
var responseContent = response.Content.ReadAsStringAsync().Result;
var responseDto = JsonConvert.DeserializeObject<GetShippingInfoByIdResponseDto>(responseContent);
// Convert Response DTO to Internal DTO
var result = new GetShippingInfoByIdDto
{
Id = responseDto.Id,
OrderId = responseDto.OrderId,
ShippingAddress = responseDto.ShippingAddress,
TrackingNumber = responseDto.TrackingNumber,
Status = ParseShippingStatus(responseDto.Status),
StatusDescription = responseDto.StatusDescription,
TotalWeight = responseDto.TotalWeight,
TotalVolume = responseDto.TotalVolume,
CreatedDate = responseDto.CreatedDate,
LastModifiedDate = responseDto.LastModifiedDate,
CreatedBy = responseDto.CreatedBy,
LastModifiedBy = responseDto.LastModifiedBy,
ShippingItems = responseDto.ShippingItems?.Select(x => new ShippingItemDto
{
ProductId = x.ProductId,
Quantity = x.Quantity,
Weight = x.Weight,
Volume = x.Volume
}).ToList()
};
_logger.LogDebug("Shipping info retrieved successfully for ID: {Id}", _params.Id);
return result;
}
else
{
_logger.LogWarning("Shipping not found for ID: {Id}. Status: {StatusCode}", _params.Id, response.StatusCode);
return null;
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error retrieving shipping info for ID: {Id}", _params.Id);
return null;
}
}
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
Error Handling: Handles success/failure responses from external system
Data Transformation: Applies business logic during mapping
4. Query Parameters - Input DTO
File: GetShippingInfoByIdParams.cs
public class GetShippingInfoByIdParams : DtoBridge
{
public string Id { get; set; }
}
What Happens:
ID Parameter: Contains the unique identifier for the entity
Simple Structure: Minimal parameters for single entity lookup
URL Binding: ID is automatically bound from URL parameter
5. Output DTO - Data Transfer Object
File: GetShippingInfoByIdDto.cs
public partial class GetShippingInfoByIdDto : 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 decimal TotalVolume { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime? LastModifiedDate { get; set; }
public string CreatedBy { get; set; }
public string LastModifiedBy { get; set; }
public ICollection<ShippingItemDto> ShippingItems { get; set; }
}
What Happens:
Complete Data: Contains all relevant fields for the entity
Detailed Structure: Includes all properties for full entity display
Related Data: May include related entity information (ShippingItems)
Audit Fields: Includes creation and modification tracking
6. AutoMapper Configuration - Data Transformation
File: GetShippingInfoByIdMapperConfiguration.cs
public partial class GetShippingInfoByIdMapperConfiguration : FlexMapperProfile
{
public GetShippingInfoByIdMapperConfiguration() : base()
{
CreateMap<Shipping, GetShippingInfoByIdDto>()
.ForMember(d => d.StatusDescription, opt => opt.MapFrom(s => s.Status.ToString()))
.ForMember(d => d.ShippingItems, opt => opt.MapFrom(s => s.ShippingItems));
}
}
What Happens:
Entity to DTO Mapping: Maps domain entities to DTOs
Custom Mappings: Handles complex property transformations
Related Data: Maps related entity properties
Computed Fields: Handles calculated properties
Key Differences from Other Query Operations
Get By ID vs Other Query Characteristics
Return Type
T
(single entity)
IEnumerable<T>
FlexiPagedList<T>
Input
ID from URL
Query parameters
Query parameters
Result Count
0 or 1
0 to many
0 to many (paginated)
Use Case
Single entity details
Dropdown/lookup
Data grids/tables
Performance
Very fast (indexed lookup)
Fast for small datasets
Optimized for large datasets
Controller Method
RunQuerySingleService
RunQueryListService
RunQueryPagedService
Query Base
FlexiQueryBridge
FlexiQueryEnumerableBridge
FlexiQueryPagedListBridge
HTTP Status
200 OK or 404 Not Found
200 OK
200 OK
Get By ID-Specific Features
Single Entity: Returns one entity or null
ID-Based Lookup: Uses unique identifier for fast retrieval
404 Handling: Returns 404 when entity not found
Complete Data: Returns all entity details
Fast Performance: Optimized for indexed lookups
Common Use Cases
Entity Details: Display detailed information about a specific shipping
Edit Forms: Load shipping data for editing
View Pages: Show complete shipping information
API Integration: Retrieve specific shipping for external systems
Validation: Check if shipping exists before operations
Flow Summary
Synchronous Flow (Data Retrieval)
GET Request β Controller receives request with ID parameter
Service Processing β Business orchestration and query resolution
Query Handler β Database query building and execution
AutoMapper β Entity-to-DTO transformation
Response β HTTP 200 OK with entity or 404 Not Found
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>().Where(t => t.Id == _params.Id);
return query;
}
Advanced Query Building
protected override IQueryable<T> Build<T>()
{
_repoFactory.Init(_params);
IQueryable<T> query = _repoFactory.GetRepo().FindAll<T>();
// Filter by ID
query = query.Where(t => t.Id == _params.Id);
// Exclude soft deleted items
query = query.Where(t => !t.IsSoftDeleted);
// Include related entities if needed
// query = query.Include(t => t.ShippingItems);
return query;
}
Error Handling Patterns
public override GetShippingInfoByIdDto Fetch()
{
var result = Build<Shipping>().SelectTo<GetShippingInfoByIdDto>().FirstOrDefault();
if (result == null)
{
_logger.LogWarning("Shipping with ID {ShippingId} not found", _params.Id);
}
else
{
_logger.LogDebug("Shipping with ID {ShippingId} retrieved successfully", _params.Id);
}
return result;
}
Performance Considerations
Optimization Strategies
Database Indexing: Ensure ID field is properly indexed
Selective Fields: Only return necessary fields in DTO
Related Data: Use Include() judiciously for related entities
Caching: Consider caching for frequently accessed entities
Query Optimization: Use FirstOrDefault() for single entity retrieval
When to Use Get By ID vs Other Queries
Entity Details Page
β Yes
β No
β No
Edit Form Loading
β Yes
β No
β No
API Integration
β Yes
β No
β No
Dropdown Population
β No
β Yes
β No
Data Grids
β No
β No
β Yes
Search Results
β No
β Yes
β Yes
Lookup Tables
β No
β Yes
β No
Error Handling
HTTP Status Codes
200 OK: Entity found and returned successfully
404 Not Found: Entity with specified ID does not exist
400 Bad Request: Invalid ID format or missing ID parameter
500 Internal Server Error: Database or server error
Controller Error Handling
[HttpGet()]
[Route("GetShippingInfoById/{id}")]
[ProducesResponseType(typeof(GetShippingInfoByIdDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> GetShippingInfoById(string id)
{
if (string.IsNullOrEmpty(id))
{
return BadRequest("Shipping ID is required");
}
GetShippingInfoByIdParams parameters = new GetShippingInfoByIdParams();
parameters.Id = id;
return RunQuerySingleService<GetShippingInfoByIdParams, GetShippingInfoByIdDto>(
parameters, _processShippingService.GetShippingInfoById);
}
Key Benefits
Performance: Very fast retrieval using indexed ID lookup
Simplicity: Simple single entity retrieval
Complete Data: Returns full entity details
Type Safety: Strongly typed DTOs and parameters
AutoMapper: Automatic entity-to-DTO mapping
Error Handling: Built-in 404 handling for missing entities
No Side Effects: Read-only operations
Testable: Each component can be tested independently
Maintainable: Clear separation of concerns
This GetShippingInfoById example demonstrates how FlexBase enables clean, maintainable, and scalable single entity retrieval operations with proper error handling and performance optimization! π
Last updated