Get By Id Example
Overview
This document demonstrates the complete Get By ID flow using the GetProductById 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 β Database β Response
Detailed Flow Breakdown
1. GET Request
β
2. Controller (API Entry Point)
β
3. Service Layer (Business Orchestration)
β
4. Query Handler (Data Retrieval)
β
5. Database (Data Query)
β
6. AutoMapper (Data Transformation)
β
7. Response (Single Entity)
Step-by-Step Implementation
1. API Controller - The Entry Point
File: ProductsController_GetProductById.cs
[HttpGet()]
[Route("GetProductById/{id}")]
[ProducesResponseType(typeof(GetProductByIdDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetProductById(string id)
{
GetProductByIdParams parameters = new GetProductByIdParams();
parameters.Id = id;
return RunQuerySingleService<GetProductByIdParams, GetProductByIdDto>(
parameters, _processProductsService.GetProductById);
}
What Happens:
HTTP Method:
GET /api/Products/GetProductById/{id}
Input: Product ID from URL parameter
Parameter Creation: Creates
GetProductByIdParams
with the IDAction: Calls the service layer to process the query
Response: HTTP 200 OK with
GetProductByIdDto
or 404 Not Found
2. Service Layer - Business Orchestration
File: ProcessProductsService_GetProductById.cs
public GetProductByIdDto GetProductById(GetProductByIdParams @params)
{
return _flexHost.GetFlexiQuery<GetProductById>().AssignParameters(@params).Fetch();
}
What Happens:
Query Resolution: Gets the FlexiQuery instance for GetProductById
Parameter Assignment: Assigns query parameters to the query handler
Query Execution: Calls the Fetch() method to execute the query
Result: Returns single product or null if not found
3. Query Handler - Data Retrieval
File: GetProductById.cs
public class GetProductById : FlexiQueryBridge<Product, GetProductByIdDto>
{
protected readonly ILogger<GetProductById> _logger;
protected GetProductByIdParams _params;
protected readonly RepoFactory _repoFactory;
public GetProductById(ILogger<GetProductById> logger, RepoFactory repoFactory)
{
_logger = logger;
_repoFactory = repoFactory;
}
public virtual GetProductById AssignParameters(GetProductByIdParams @params)
{
_params = @params;
return this;
}
public override GetProductByIdDto Fetch()
{
var result = Build<Product>().SelectTo<GetProductByIdDto>().FirstOrDefault();
return result;
}
protected override IQueryable<T> Build<T>()
{
_repoFactory.Init(_params);
IQueryable<T> query = _repoFactory.GetRepo().FindAll<T>().Where(t => t.Id == _params.Id);
return query;
}
}
What Happens:
Parameter Assignment: Stores query parameters for use in query building
Query Building: Creates the database query with ID filtering
Data Projection: Uses AutoMapper to project to DTO
Single Result: Returns first matching entity or null
ID Filtering: Filters by the specific ID parameter
4. Query Parameters - Input DTO
File: GetProductByIdParams.cs
public class GetProductByIdParams : 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: GetProductByIdDto.cs
public partial class GetProductByIdDto : DtoBridge
{
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public string CategoryId { get; set; }
public string CategoryName { get; set; }
public string Sku { get; set; }
public int StockQuantity { get; set; }
public bool IsActive { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime? LastModifiedDate { get; set; }
public string CreatedBy { get; set; }
public string LastModifiedBy { 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 (CategoryName)
Audit Fields: Includes creation and modification tracking
6. AutoMapper Configuration - Data Transformation
File: GetProductByIdMapperConfiguration.cs
public partial class GetProductByIdMapperConfiguration : FlexMapperProfile
{
public GetProductByIdMapperConfiguration() : base()
{
CreateMap<Product, GetProductByIdDto>()
.ForMember(d => d.CategoryName, opt => opt.MapFrom(s => s.Category.Name))
.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
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 entity
Edit Forms: Load entity data for editing
View Pages: Show complete entity information
API Integration: Retrieve specific entity for external systems
Validation: Check if entity 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.Category);
return query;
}
Error Handling Patterns
public override GetProductByIdDto Fetch()
{
var result = Build<Product>().SelectTo<GetProductByIdDto>().FirstOrDefault();
if (result == null)
{
_logger.LogWarning("Product with ID {ProductId} not found", _params.Id);
}
else
{
_logger.LogDebug("Product with ID {ProductId} 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("GetProductById/{id}")]
[ProducesResponseType(typeof(GetProductByIdDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> GetProductById(string id)
{
if (string.IsNullOrEmpty(id))
{
return BadRequest("Product ID is required");
}
GetProductByIdParams parameters = new GetProductByIdParams();
parameters.Id = id;
return RunQuerySingleService<GetProductByIdParams, GetProductByIdDto>(
parameters, _processProductsService.GetProductById);
}
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 GetProductById example demonstrates how FlexBase enables clean, maintainable, and scalable single entity retrieval operations with proper error handling and performance optimization! π
Last updated